diff --git a/GEMINI.md b/GEMINI.md index 789f2d7..5d7c4ab 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -18,6 +18,7 @@ This project is makuro-base-template, a high-performance, full-stack React devel * **Install dependencies**: `bun install` * **Start development server**: `bun run dev` (Runs Elysia + Vite Middleware) * **Update Route Tree**: `bun x tsr generate` (usually automatic via Vite plugin) +* **Generate API Types**: `bun run gen:api` (Generates types from OpenAPI schema) * **Database Migration**: `bun x prisma migrate dev` ### Production diff --git a/bun.lock b/bun.lock index 526caa8..216a34c 100644 --- a/bun.lock +++ b/bun.lock @@ -7,7 +7,6 @@ "dependencies": { "@better-auth/cli": "^1.4.18", "@elysiajs/cors": "^1.4.1", - "@elysiajs/eden": "^1.4.6", "@elysiajs/swagger": "^1.3.1", "@mantine/core": "^8.3.14", "@mantine/dates": "^8.3.13", @@ -20,6 +19,7 @@ "better-auth": "^1.4.18", "dayjs": "^1.11.19", "elysia": "^1.4.22", + "openapi-fetch": "^0.15.0", "pino": "^10.3.0", "pino-pretty": "^13.1.3", "react": "^19", @@ -41,6 +41,7 @@ "@vitejs/plugin-react": "^5.1.3", "concurrently": "^9.2.1", "fast-glob": "^3.3.3", + "openapi-typescript": "^7.10.1", "postcss": "^8.5.6", "postcss-preset-mantine": "^1.18.0", "postcss-simple-vars": "^7.0.1", @@ -167,8 +168,6 @@ "@elysiajs/cors": ["@elysiajs/cors@1.4.1", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-lQfad+F3r4mNwsxRKbXyJB8Jg43oAOXjRwn7sKUL6bcOW3KjUqUimTS+woNpO97efpzjtDE0tEjGk9DTw8lqTQ=="], - "@elysiajs/eden": ["@elysiajs/eden@1.4.6", "", { "peerDependencies": { "elysia": ">=1.4.19" } }, "sha512-Tsa4NwXEWg/u73vWiYZQ3L5/ecgZSxqiEjYwpS+4qBKXeTZqZKl2hcgHJSVBL+InEDMi35Xugct7qyAXE5oM4Q=="], - "@elysiajs/swagger": ["@elysiajs/swagger@1.3.1", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-LcbLHa0zE6FJKWPWKsIC/f+62wbDv3aXydqcNPVPyqNcaUgwvCajIi+5kHEU6GO3oXUCpzKaMsb3gsjt8sLzFQ=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], @@ -295,6 +294,12 @@ "@react-dev-inspector/vite-plugin": ["@react-dev-inspector/vite-plugin@2.0.1", "", { "dependencies": { "@react-dev-inspector/middleware": "2.0.1" } }, "sha512-J1eI7cIm2IXE6EwhHR1OyoefvobUJEn/vJWEBwOM5uW4JkkLwuVoV9vk++XJyAmKUNQ87gdWZvSWrI2LjfrSug=="], + "@redocly/ajv": ["@redocly/ajv@8.17.3", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-NQsbJbB/GV7JVO88ebFkMndrnuGp/dTm5/2NISeg+JGcLzTfGBJZ01+V5zD8nKBOpi/dLLNFT+Ql6IcUk8ehng=="], + + "@redocly/config": ["@redocly/config@0.22.2", "", {}, "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ=="], + + "@redocly/openapi-core": ["@redocly/openapi-core@1.34.6", "", { "dependencies": { "@redocly/ajv": "^8.11.2", "@redocly/config": "^0.22.0", "colorette": "^1.2.0", "https-proxy-agent": "^7.0.5", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "minimatch": "^5.0.1", "pluralize": "^8.0.0", "yaml-ast-parser": "0.0.43" } }, "sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.2", "", {}, "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], @@ -465,12 +470,16 @@ "address": ["address@1.2.2", "", {}, "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="], "ajv-keywords": ["ajv-keywords@5.1.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "ajv": "^8.8.2" } }, "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw=="], + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + "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=="], @@ -479,6 +488,8 @@ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], @@ -507,7 +518,7 @@ "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -531,6 +542,8 @@ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="], + "chevrotain": ["chevrotain@10.5.0", "", { "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "@chevrotain/utils": "10.5.0", "lodash": "4.17.21", "regexp-to-ast": "0.5.0" } }, "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A=="], "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -735,6 +748,8 @@ "hotkeys-js": ["hotkeys-js@3.13.15", "", {}, "sha512-gHh8a/cPTCpanraePpjRxyIlxDFrIhYqjuh01UHWEwDpglJKCnvLW8kqSx5gQtOuSsJogNZXLhOdbSExpgUiqg=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -743,6 +758,8 @@ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + "index-to-position": ["index-to-position@1.2.0", "", {}, "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw=="], + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -781,8 +798,12 @@ "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + "js-levenshtein": ["js-levenshtein@1.1.6", "", {}, "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], @@ -829,7 +850,7 @@ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -863,8 +884,14 @@ "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + "openapi-fetch": ["openapi-fetch@0.15.0", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.15" } }, "sha512-OjQUdi61WO4HYhr9+byCPMj0+bgste/LtSBEcV6FzDdONTs7x0fWn8/ndoYwzqCsKWIxEZwo4FN/TG1c1rI8IQ=="], + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + "openapi-typescript": ["openapi-typescript@7.10.1", "", { "dependencies": { "@redocly/openapi-core": "^1.34.5", "ansi-colors": "^4.1.3", "change-case": "^5.4.4", "parse-json": "^8.3.0", "supports-color": "^10.2.2", "yargs-parser": "^21.1.1" }, "peerDependencies": { "typescript": "^5.x" }, "bin": { "openapi-typescript": "bin/cli.js" } }, "sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw=="], + + "openapi-typescript-helpers": ["openapi-typescript-helpers@0.0.15", "", {}, "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -873,7 +900,7 @@ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "parse-json": ["parse-json@8.3.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "index-to-position": "^1.1.0", "type-fest": "^4.39.1" } }, "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ=="], "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], @@ -919,6 +946,8 @@ "pkg-up": ["pkg-up@3.1.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="], + "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], + "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=="], "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], @@ -1177,6 +1206,8 @@ "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + "yaml-ast-parser": ["yaml-ast-parser@0.0.43", "", {}, "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="], + "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=="], @@ -1207,6 +1238,8 @@ "@react-dev-inspector/babel-plugin/@babel/types": ["@babel/types@7.20.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg=="], + "@redocly/openapi-core/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="], + "@scalar/themes/@scalar/types": ["@scalar/types@0.1.7", "", { "dependencies": { "@scalar/openapi-types": "0.2.0", "@unhead/schema": "^1.11.11", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "zod": "^3.23.8" } }, "sha512-irIDYzTQG2KLvFbuTI8k2Pz/R4JR+zUUSykVTbEMatkzMmVFnn1VzNSMlODbadycwZunbnL2tA27AXed9URVjw=="], "@tanstack/router-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -1221,10 +1254,14 @@ "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "cosmiconfig/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "detect-port-alt/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + "fork-ts-checker-webpack-plugin/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "fork-ts-checker-webpack-plugin/schema-utils": ["schema-utils@2.7.0", "", { "dependencies": { "@types/json-schema": "^7.0.4", "ajv": "^6.12.2", "ajv-keywords": "^3.4.1" } }, "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A=="], "fork-ts-checker-webpack-plugin/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -1233,6 +1270,8 @@ "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "global-prefix/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], @@ -1245,6 +1284,8 @@ "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "openapi-typescript/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + "pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -1261,6 +1302,8 @@ "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "recursive-readdir/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], @@ -1283,14 +1326,20 @@ "detect-port-alt/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "fork-ts-checker-webpack-plugin/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "fork-ts-checker-webpack-plugin/schema-utils/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=="], "fork-ts-checker-webpack-plugin/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], + "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], "react-dev-utils/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], + "recursive-readdir/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "@prisma/config/c12/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "fork-ts-checker-webpack-plugin/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], diff --git a/generated/api.ts b/generated/api.ts new file mode 100644 index 0000000..51c4f92 --- /dev/null +++ b/generated/api.ts @@ -0,0 +1,501 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/api/session": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiSession"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/apikey/": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get all API keys + * @description Get all API keys + */ + get: operations["getApiApikey"]; + put?: never; + /** + * Create a new API key + * @description Create a new API key + */ + post: operations["postApiApikey"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/apikey/update": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Update an API key + * @description Update an API key + */ + post: operations["postApiApikeyUpdate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/apikey/delete": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Delete an API key + * @description Delete an API key + */ + post: operations["postApiApikeyDelete"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: never; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + getApiSession: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + getApiApikey: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + apiKeys: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }[]; + }; + "multipart/form-data": { + apiKeys: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }[]; + }; + "text/plain": { + apiKeys: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }[]; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + }; + }; + postApiApikey: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + expiresAt?: string; + }; + "multipart/form-data": { + name: string; + expiresAt?: string; + }; + "text/plain": { + name: string; + expiresAt?: string; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + apiKey: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }; + }; + "multipart/form-data": { + apiKey: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }; + }; + "text/plain": { + apiKey: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + }; + }; + postApiApikeyUpdate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + id: string; + isActive: boolean; + expiresAt?: string | null; + }; + "multipart/form-data": { + id: string; + isActive: boolean; + expiresAt?: string | null; + }; + "text/plain": { + id: string; + isActive: boolean; + expiresAt?: string | null; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + apiKey: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }; + }; + "multipart/form-data": { + apiKey: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }; + }; + "text/plain": { + apiKey: { + id: string; + name: string; + key: string; + isActive: boolean; + expiresAt: string | null | unknown; + createdAt: unknown; + updatedAt: unknown; + }; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + }; + }; + postApiApikeyDelete: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + id: string; + }; + "multipart/form-data": { + id: string; + }; + "text/plain": { + id: string; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + success: boolean; + }; + "multipart/form-data": { + success: boolean; + }; + "text/plain": { + success: boolean; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + "multipart/form-data": { + error: string; + }; + "text/plain": { + error: string; + }; + }; + }; + }; + }; +} diff --git a/generated/schema.json b/generated/schema.json new file mode 100644 index 0000000..1c61a0d --- /dev/null +++ b/generated/schema.json @@ -0,0 +1,1176 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Bun + React API", + "description": "Development documentation", + "version": "1.0.0" + }, + "paths": { + "/api/session": { + "get": { + "operationId": "getApiSession", + "responses": { + "200": {} + } + } + }, + "/api/apikey/": { + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "apiKeys": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + } + }, + "required": [ + "apiKeys" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "apiKeys": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + } + }, + "required": [ + "apiKeys" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "apiKeys": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + } + }, + "required": [ + "apiKeys" + ] + } + } + } + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "operationId": "getApiApikey", + "summary": "Get all API keys", + "description": "Get all API keys" + }, + "post": { + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "apiKey": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + }, + "required": [ + "apiKey" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "apiKey": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + }, + "required": [ + "apiKey" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "apiKey": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + }, + "required": [ + "apiKey" + ] + } + } + } + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "operationId": "postApiApikey", + "summary": "Create a new API key", + "description": "Create a new API key", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "expiresAt": { + "type": "string" + } + } + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "expiresAt": { + "type": "string" + } + } + } + }, + "text/plain": { + "schema": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "expiresAt": { + "type": "string" + } + } + } + } + } + } + } + }, + "/api/apikey/update": { + "post": { + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "apiKey": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + }, + "required": [ + "apiKey" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "apiKey": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + }, + "required": [ + "apiKey" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "apiKey": { + "type": "object", + "required": [ + "id", + "name", + "key", + "isActive", + "expiresAt", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "key": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + }, + {} + ] + }, + "createdAt": {}, + "updatedAt": {} + } + } + }, + "required": [ + "apiKey" + ] + } + } + } + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "operationId": "postApiApikeyUpdate", + "summary": "Update an API key", + "description": "Update an API key", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "id", + "isActive" + ], + "properties": { + "id": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "id", + "isActive" + ], + "properties": { + "id": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + } + }, + "text/plain": { + "schema": { + "type": "object", + "required": [ + "id", + "isActive" + ], + "properties": { + "id": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "expiresAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + } + } + } + } + } + }, + "/api/apikey/delete": { + "post": { + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + }, + "text/plain": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "operationId": "postApiApikeyDelete", + "summary": "Delete an API key", + "description": "Delete an API key", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + }, + "text/plain": { + "schema": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": {} + } +} \ No newline at end of file diff --git a/package.json b/package.json index 3e3e6cc..ec5f4c7 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,13 @@ "lint": "biome check .", "check": "biome check --write .", "format": "biome format --write .", + "gen:api": "bun scripts/generate-schema.ts && bun x openapi-typescript generated/schema.json -o generated/api.ts", "build": "bun build ./src/index.html --outdir=dist --sourcemap --target=browser --minify --define:process.env.NODE_ENV='\"production\"' --env='VITE_*'", "start": "NODE_ENV=production bun src/index.ts" }, "dependencies": { "@better-auth/cli": "^1.4.18", "@elysiajs/cors": "^1.4.1", - "@elysiajs/eden": "^1.4.6", "@elysiajs/swagger": "^1.3.1", "@mantine/core": "^8.3.14", "@mantine/dates": "^8.3.13", @@ -30,6 +30,7 @@ "better-auth": "^1.4.18", "dayjs": "^1.11.19", "elysia": "^1.4.22", + "openapi-fetch": "^0.15.0", "pino": "^10.3.0", "pino-pretty": "^13.1.3", "react": "^19", @@ -37,25 +38,26 @@ "valtio": "^2.3.0" }, "devDependencies": { - "@react-dev-inspector/vite-plugin": "^2.0.1", - "react-dev-inspector": "^2.0.1", "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@biomejs/biome": "2.3.14", + "@react-dev-inspector/vite-plugin": "^2.0.1", "@tanstack/react-router-devtools": "^1.158.1", + "@tanstack/router-cli": "^1.157.16", + "@tanstack/router-plugin": "^1.157.16", "@tanstack/router-vite-plugin": "^1.158.1", "@types/bun": "latest", "@types/react": "^19", "@types/react-dom": "^19", "@vitejs/plugin-react": "^5.1.3", + "concurrently": "^9.2.1", + "fast-glob": "^3.3.3", + "openapi-typescript": "^7.10.1", "postcss": "^8.5.6", "postcss-preset-mantine": "^1.18.0", "postcss-simple-vars": "^7.0.1", - "vite": "^7.3.1", "prisma": "^6.19.2", - "@tanstack/router-cli": "^1.157.16", - "@tanstack/router-plugin": "^1.157.16", - "concurrently": "^9.2.1", - "fast-glob": "^3.3.3" + "react-dev-inspector": "^2.0.1", + "vite": "^7.3.1" } } diff --git a/scripts/generate-schema.ts b/scripts/generate-schema.ts new file mode 100644 index 0000000..e534573 --- /dev/null +++ b/scripts/generate-schema.ts @@ -0,0 +1,7 @@ +import api from "../src/api"; + +const response = await api.handle(new Request("http://localhost/api/docs/json")); +const schema = await response.json(); + +await Bun.write("generated/schema.json", JSON.stringify(schema, null, 2)); +console.log("✅ OpenAPI schema generated at generated/schema.json"); diff --git a/src/api/apikey.ts b/src/api/apikey.ts index acb23ff..e696ba5 100644 --- a/src/api/apikey.ts +++ b/src/api/apikey.ts @@ -45,6 +45,23 @@ export const apikey = new Elysia({ } }, { + response: { + 200: t.Object({ + apiKeys: t.Array( + t.Object({ + id: t.String(), + name: t.String(), + key: t.String(), + isActive: t.Boolean(), + expiresAt: t.Union([t.String(), t.Null(), t.Any()]), + createdAt: t.Any(), + updatedAt: t.Any(), + }), + ), + }), + 401: t.Object({ error: t.String() }), + 500: t.Object({ error: t.String() }), + }, detail: { summary: "Get all API keys", description: "Get all API keys", @@ -97,6 +114,21 @@ export const apikey = new Elysia({ name: t.String(), expiresAt: t.Optional(t.String()), // ISO date string }), + response: { + 200: t.Object({ + apiKey: t.Object({ + id: t.String(), + name: t.String(), + key: t.String(), + isActive: t.Boolean(), + expiresAt: t.Union([t.String(), t.Null(), t.Any()]), + createdAt: t.Any(), + updatedAt: t.Any(), + }), + }), + 401: t.Object({ error: t.String() }), + 500: t.Object({ error: t.String() }), + }, detail: { summary: "Create a new API key", description: "Create a new API key", @@ -174,6 +206,22 @@ export const apikey = new Elysia({ isActive: t.Boolean(), expiresAt: t.Optional(t.Union([t.String(), t.Null()])), // ISO date string or null }), + response: { + 200: t.Object({ + apiKey: t.Object({ + id: t.String(), + name: t.String(), + key: t.String(), + isActive: t.Boolean(), + expiresAt: t.Union([t.String(), t.Null(), t.Any()]), + createdAt: t.Any(), + updatedAt: t.Any(), + }), + }), + 401: t.Object({ error: t.String() }), + 403: t.Object({ error: t.String() }), + 500: t.Object({ error: t.String() }), + }, detail: { summary: "Update an API key", description: "Update an API key", @@ -225,6 +273,14 @@ export const apikey = new Elysia({ body: t.Object({ id: t.String(), }), + response: { + 200: t.Object({ + success: t.Boolean(), + }), + 500: t.Object({ error: t.String() }), + 403: t.Object({ error: t.String() }), + 401: t.Object({ error: t.String() }), + }, detail: { summary: "Delete an API key", description: "Delete an API key", diff --git a/src/middleware/apiMiddleware.tsx b/src/middleware/apiMiddleware.tsx index c70160a..3467c77 100644 --- a/src/middleware/apiMiddleware.tsx +++ b/src/middleware/apiMiddleware.tsx @@ -86,6 +86,11 @@ export function apiMiddleware(app: Elysia) { } }) .onBeforeHandle(({ user, set, request }) => { + const url = new URL(request.url); + if (url.pathname.startsWith("/api/docs")) { + return; + } + if (!user) { logger.warn(`[AUTH] Unauthorized: ${request.method} ${request.url}`); set.status = 401; diff --git a/src/routes/dashboard/apikey.tsx b/src/routes/dashboard/apikey.tsx index 30bf548..aea1148 100644 --- a/src/routes/dashboard/apikey.tsx +++ b/src/routes/dashboard/apikey.tsx @@ -68,9 +68,12 @@ function DashboardApikeyComponent() { const fetchApiKeys = useCallback(async () => { try { setLoading(true); - const response = await apiClient.api.apikey.get(); - if (response.data) { - setApiKeys((response.data.apiKeys as any) || []); + const { data, error } = await apiClient.GET("/api/apikey/"); + if (data) { + setApiKeys((data.apiKeys as any) || []); + } + if (error) { + setError("Failed to load API keys"); } } catch (err) { setError("Failed to load API keys"); @@ -92,19 +95,24 @@ function DashboardApikeyComponent() { try { setCreating(true); - const response = await apiClient.api.apikey.post({ - name: newKeyName, - expiresAt: newKeyExpiresAt - ? dayjs(newKeyExpiresAt).toISOString() - : undefined, + const { data, error } = await apiClient.POST("/api/apikey/", { + body: { + name: newKeyName, + expiresAt: newKeyExpiresAt + ? dayjs(newKeyExpiresAt).toISOString() + : undefined, + }, }); - if (response.data) { - setApiKeys([...apiKeys, response.data.apiKey as any]); + if (data) { + setApiKeys([...apiKeys, data.apiKey as any]); setNewKeyName(""); setNewKeyExpiresAt(null); setCreateModalOpen(false); } + if (error) { + setError("Failed to create API key"); + } } catch (err) { setError("Failed to create API key"); console.error(err); @@ -119,18 +127,23 @@ function DashboardApikeyComponent() { setError("API key ID is required"); return; } - const response = await apiClient.api.apikey.update.post({ - id, - isActive: !currentStatus, + const { data, error } = await apiClient.POST("/api/apikey/update", { + body: { + id, + isActive: !currentStatus, + }, }); - if (response.data) { + if (data) { setApiKeys( apiKeys.map((key) => key.id === id ? { ...key, isActive: !currentStatus } : key, ), ); } + if (error) { + setError("Failed to update API key status"); + } } catch (err) { setError("Failed to update API key status"); console.error(err); @@ -147,12 +160,18 @@ function DashboardApikeyComponent() { if (!keyToDelete) return; try { - await apiClient.api.apikey.delete.post({ - id: keyToDelete, + const { error } = await apiClient.POST("/api/apikey/delete", { + body: { + id: keyToDelete, + }, }); - setApiKeys(apiKeys.filter((key: ApiKey) => key.id !== keyToDelete)); - setDeleteModalOpen(false); - setKeyToDelete(null); + if (!error) { + setApiKeys(apiKeys.filter((key: ApiKey) => key.id !== keyToDelete)); + setDeleteModalOpen(false); + setKeyToDelete(null); + } else { + setError("Failed to delete API key"); + } } catch (err) { setError("Failed to delete API key"); console.error(err); diff --git a/src/utils/api-client.ts b/src/utils/api-client.ts index f8db9a4..e20b6e6 100644 --- a/src/utils/api-client.ts +++ b/src/utils/api-client.ts @@ -1,5 +1,5 @@ -import { edenTreaty } from "@elysiajs/eden"; -import type { ApiApp } from "../index"; +import createClient from "openapi-fetch"; +import type { paths } from "../../generated/api"; import { VITE_PUBLIC_URL } from "./env"; const baseUrl = @@ -8,4 +8,7 @@ const baseUrl = ? window.location.origin : "http://localhost:3000"); -export const apiClient = edenTreaty(baseUrl); +export const apiClient = createClient({ + baseUrl: baseUrl, + credentials: "include", +});