diff --git a/docker-compose.redis.yml b/docker-compose.redis.yml new file mode 100644 index 00000000..a3e59940 --- /dev/null +++ b/docker-compose.redis.yml @@ -0,0 +1,11 @@ +services: + redis: + image: redis:latest + hostname: redis + restart: unless-stopped + ports: + - 6379:6379 + volumes: + - redis_data:/data +volumes: + redis_data: diff --git a/examples/with-elysia/index.ts b/examples/with-elysia/index.ts new file mode 100644 index 00000000..af76637e --- /dev/null +++ b/examples/with-elysia/index.ts @@ -0,0 +1,66 @@ +import { createBullBoard } from '@bull-board/api'; +import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'; +import { ElysiaAdapter } from '@bull-board/elysia'; +import { Queue as QueueMQ, Worker } from 'bullmq'; +import Elysia from 'elysia'; + +const sleep = (t: number) => new Promise((resolve) => setTimeout(resolve, t * 1000)); + +const redisOptions = { + port: 6379, + host: 'localhost', + password: '', +}; + +const createQueueMQ = (name: string) => new QueueMQ(name, { connection: redisOptions }); + +async function setupBullMQProcessor(queueName: string) { + new Worker( + queueName, + async (job) => { + for (let i = 0; i <= 100; i++) { + await sleep(Math.random()); + await job.updateProgress(i); + await job.log(`Processing job at interval ${i}`); + + if (Math.random() * 200 < 1) throw new Error(`Random error ${i}`); + } + + return { jobId: `This is the return value of job (${job.id})` }; + }, + { connection: redisOptions } + ); +} + +const exampleBullMq = createQueueMQ('BullMQ'); + +await setupBullMQProcessor(exampleBullMq.name); + +const serverAdapter = new ElysiaAdapter('/ui'); + +createBullBoard({ + queues: [new BullMQAdapter(exampleBullMq)], + serverAdapter, +}); + +const app = new Elysia() + .onError(({ error, code, request }) => { + console.error(error, code, request.method, request.url); + if(code === "NOT_FOUND") return "NOT_FOUND"; + }) + .use(serverAdapter.registerPlugin()) + .get('/add', async ({ query }) => { + await exampleBullMq.add('Add', { title: query.title }); + + return { ok: true }; + }); + +app.listen(3000, ({ port, url }) => { + /* eslint-disable no-console */ + console.log(`Running on ${url.hostname}:${port}...`); + console.log(`For the UI of instance1, open http://localhost:${port}/ui`); + console.log('Make sure Redis is running on port 6379 by default'); + console.log('To populate the queue, run:'); + console.log(` curl http://localhost:${port}/add?title=Example`); + /* eslint-enable no-console */ +}); diff --git a/examples/with-elysia/package.json b/examples/with-elysia/package.json new file mode 100644 index 00000000..0e887556 --- /dev/null +++ b/examples/with-elysia/package.json @@ -0,0 +1,18 @@ +{ + "name": "bull-board-with-elysia", + "type": "module", + "version": "1.0.0", + "description": "Example of how to use Elysia server with bull-board", + "module": "index.ts", + "scripts": { + "dev": "bun --watch index.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "kravetsone", + "license": "ISC", + "dependencies": { + "@bull-board/elysia": "^5.20.1", + "bullmq": "^5.13.2", + "elysia": "^1.1.24" + } +} diff --git a/examples/with-elysia/tsconfig.json b/examples/with-elysia/tsconfig.json new file mode 100644 index 00000000..48b1d794 --- /dev/null +++ b/examples/with-elysia/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "lib": ["ESNext", "DOM"], + "module": "ESNext", + "target": "ESNext", + "moduleResolution": "Bundler", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "noUncheckedIndexedAccess": true, + "verbatimModuleSyntax": true, + "rootDir": "./src", + "noEmit": true + } +} diff --git a/examples/with-elysia/yarn.lock b/examples/with-elysia/yarn.lock new file mode 100644 index 00000000..4a575e84 --- /dev/null +++ b/examples/with-elysia/yarn.lock @@ -0,0 +1,210 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz#9edec61b22c3082018a79f6d1c30289ddf3d9d11" + integrity sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz#33677a275204898ad8acbf62734fc4dc0b6a4855" + integrity sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz#19edf7cdc2e7063ee328403c1d895a86dd28f4bb" + integrity sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz#94fb0543ba2e28766c3fc439cabbe0440ae70159" + integrity sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz#4a0609ab5fe44d07c9c60a11e4484d3c38bbd6e3" + integrity sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" + integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== + +"@sinclair/typebox@0.32.34": + version "0.32.34" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.32.34.tgz#a1c59d4df30982263cc7aa64c2c853878050838d" + integrity sha512-a3Z3ytYl6R/+7ldxx04PO1semkwWlX/8pTqxsPw4quIcIXDFPZhOc1Wx8azWmkU26ccK3mHwcWenn0avNgAKQg== + +bullmq@^5.13.2: + version "5.25.6" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-5.25.6.tgz#7a7bb38e48556e96cba1a4c6988679ed6e194d06" + integrity sha512-jxpa/DB02V20CqBAgyqpQazT630CJm0r4fky8EchH3mcJAomRtKXLS6tRA0J8tb29BDGlr/LXhlUuZwdBJBSdA== + dependencies: + cron-parser "^4.6.0" + ioredis "^5.4.1" + msgpackr "^1.11.2" + node-abort-controller "^3.1.1" + semver "^7.5.4" + tslib "^2.0.0" + uuid "^9.0.0" + +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + +cookie@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.1.tgz#e1a00d20420e0266aff817815640289eef142751" + integrity sha512-Xd8lFX4LM9QEEwxQpF9J9NTUh8pmdJO0cyRJhFiDoLTk2eH8FXlRv2IFGYVadZpqI3j8fhNrSdKCeYPxiAhLXw== + +cron-parser@^4.6.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.9.0.tgz#0340694af3e46a0894978c6f52a6dbb5c0f11ad5" + integrity sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q== + dependencies: + luxon "^3.2.1" + +debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + +detect-libc@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +elysia@^1.1.24: + version "1.1.24" + resolved "https://registry.yarnpkg.com/elysia/-/elysia-1.1.24.tgz#093eeb60c8338e4e14bae33e3718b54682dbcabe" + integrity sha512-viJOb+PADrxJn7ntlQfoXsLuVgHNS09gn9dj2yroHnwqheN0hZp81WCGRQXkvh4kW5zGISX50/eqAcoNDpdL/g== + dependencies: + "@sinclair/typebox" "0.32.34" + cookie "^1.0.1" + fast-decode-uri-component "^1.0.1" + openapi-types "^12.1.3" + +fast-decode-uri-component@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" + integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== + +ioredis@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.1.tgz#1c56b70b759f01465913887375ed809134296f40" + integrity sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + +luxon@^3.2.1: + version "3.4.4" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" + integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +msgpackr-extract@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz#e9d87023de39ce714872f9e9504e3c1996d61012" + integrity sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA== + dependencies: + node-gyp-build-optional-packages "5.2.2" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3" + +msgpackr@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.2.tgz#4463b7f7d68f2e24865c395664973562ad24473d" + integrity sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g== + optionalDependencies: + msgpackr-extract "^3.0.2" + +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + +node-gyp-build-optional-packages@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz#522f50c2d53134d7f3a76cd7255de4ab6c96a3a4" + integrity sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw== + dependencies: + detect-libc "^2.0.1" + +openapi-types@^12.1.3: + version "12.1.3" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" + integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== + +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== + dependencies: + redis-errors "^1.0.0" + +semver@^7.5.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + +tslib@^2.0.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== diff --git a/packages/elysia/package.json b/packages/elysia/package.json new file mode 100644 index 00000000..f79706c6 --- /dev/null +++ b/packages/elysia/package.json @@ -0,0 +1,52 @@ +{ + "name": "@bull-board/elysia", + "type": "commonjs", + "version": "5.20.1", + "description": "A Elysia server adapter for Bull-Board dashboard.", + "keywords": [ + "bull", + "bullmq", + "redis", + "elysia", + "elysia-plugin", + "adapter", + "queue", + "monitoring", + "dashboard" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/felixmosh/bull-board.git", + "directory": "packages/elysia" + }, + "license": "MIT", + "author": { + "name": "kravetsone", + "url": "https://github.com/kravetsone" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "clean": "rm -rf dist" + }, + "dependencies": { + "@bull-board/api": "6.4.1", + "@bull-board/ui": "6.4.1", + "ejs": "^3.1.10" + }, + "peerDependencies": { + "elysia": "^1.1.0" + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/bun": "^1.1.13", + "@types/ejs": "^3.1.5", + "elysia": "^1.1.24" + } +} diff --git a/packages/elysia/src/ElysiaAdapter.ts b/packages/elysia/src/ElysiaAdapter.ts new file mode 100644 index 00000000..5d260c7e --- /dev/null +++ b/packages/elysia/src/ElysiaAdapter.ts @@ -0,0 +1,161 @@ +import type { + AppControllerRoute, + AppViewRoute, + BullBoardQueues, + ControllerHandlerReturnType, + HTTPMethod, + IServerAdapter, + UIConfig, +} from '@bull-board/api/dist/typings/app'; +import ejs from 'ejs'; +import { Elysia } from 'elysia'; + +export class ElysiaAdapter implements IServerAdapter { + private plugin = new Elysia({ + name: '@bull-board/elysia', + }).as('plugin'); + private basePath = ''; + + constructor(basePath = '') { + if (basePath.length) { + // it shows prefix is "" in types + this.basePath = basePath; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.plugin.config.prefix = this.basePath; + } + } + + private entryRoute: AppViewRoute | undefined; + private statics: { path: string; route: string } | undefined; + private bullBoardQueues: BullBoardQueues | undefined; + private viewPath: string | undefined; + private uiConfig: UIConfig = {}; + + public setStaticPath(staticsRoute: string, staticsPath: string): ElysiaAdapter { + this.statics = { route: staticsRoute, path: staticsPath }; + + return this; + } + + public setViewsPath(viewPath: string): ElysiaAdapter { + this.viewPath = viewPath; + + return this; + } + + public setErrorHandler(handler: (error: Error) => ControllerHandlerReturnType) { + this.plugin.onError(({ error, set }) => { + const response = handler(error); + set.status = response.status || 500; + + return response.body; + }); + + return this; + } + + public setApiRoutes(routes: AppControllerRoute[]): ElysiaAdapter { + for (const { route, handler, method } of routes) { + const methods = Array.isArray(method) ? method : [method]; + + for (const method of methods) { + this.registerRoute(route, method, handler); + } + } + + return this; + } + + public setEntryRoute(routeDef: AppViewRoute): ElysiaAdapter { + this.entryRoute = routeDef; + + return this; + } + + public setQueues(bullBoardQueues: BullBoardQueues): ElysiaAdapter { + this.bullBoardQueues = bullBoardQueues; + return this; + } + + public setUIConfig(config: UIConfig = {}): ElysiaAdapter { + this.uiConfig = config; + + return this; + } + + public registerPlugin() { + if (!this.statics) { + throw new Error(`Please call 'setStaticPath' before using 'registerHandlers'`); + } + if (!this.entryRoute) { + throw new Error(`Please call 'setEntryRoute' before using 'registerHandlers'`); + } + if (!this.viewPath) { + throw new Error(`Please call 'setViewsPath' before using 'registerHandlers'`); + } + + if (!this.uiConfig) { + throw new Error(`Please call 'setUIConfig' before using 'registerHandlers'`); + } + + const { method, route, handler } = this.entryRoute; + + const routes = Array.isArray(route) ? route : [route]; + + for (const route of routes) { + this.plugin.route(method.toUpperCase(), route, async () => { + const { name: filename, params } = handler({ + basePath: this.basePath, + uiConfig: this.uiConfig, + }); + + return new Response(await ejs.renderFile(`${this.viewPath}/${filename}`, params), { + headers: { + 'content-type': 'text/html', + }, + }); + }); + } + + const glob = new Bun.Glob(`${this.statics.path}/**/*`); + for (const path of glob.scanSync()) { + this.plugin.get( + // TODO: maybe recode this + path.substring(path.indexOf('dist') + 4).replaceAll('\\', '/'), + () => new Response(Bun.file(path)) + ); + } + + return this.plugin.as('plugin'); + } + + private registerRoute( + routeOrRoutes: string | string[], + method: HTTPMethod, + handler: AppControllerRoute['handler'] + ) { + const { bullBoardQueues } = this; + + if (!bullBoardQueues) { + throw new Error(`Please call 'setQueues' before using 'registerHandlers'`); + } + + const routes = Array.isArray(routeOrRoutes) ? routeOrRoutes : [routeOrRoutes]; + + for (const route of routes) { + this.plugin.route(method.toUpperCase(), route, async ({ params, body, query, set }) => { + const response = await handler({ + queues: this.bullBoardQueues as BullBoardQueues, + params, + body: body as Record, + query, + }); + + if (response.status) set.status = response.status; + + return response.body; + }); + } + } +} diff --git a/packages/elysia/src/index.ts b/packages/elysia/src/index.ts new file mode 100644 index 00000000..a021aa47 --- /dev/null +++ b/packages/elysia/src/index.ts @@ -0,0 +1 @@ +export { ElysiaAdapter } from './ElysiaAdapter'; diff --git a/packages/elysia/tsconfig.json b/packages/elysia/tsconfig.json new file mode 100644 index 00000000..5ce2210e --- /dev/null +++ b/packages/elysia/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "outDir": "dist", + "esModuleInterop": true, + "lib": [ + "es2019", + "DOM" + ], + "module": "CommonJS", + "moduleResolution": "node", + "noImplicitAny": true, + "sourceMap": true, + "strict": true, + "target": "es2019", + "noUnusedParameters": true, + "noUnusedLocals": true, + "resolveJsonModule": true, + "declaration": true, + "skipLibCheck": true + }, + "include": [ + "./src", + "./typings/*.d.ts" + ] + } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index dbc4e818..160ae839 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3063,6 +3063,11 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== +"@sinclair/typebox@0.32.34": + version "0.32.34" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.32.34.tgz#a1c59d4df30982263cc7aa64c2c853878050838d" + integrity sha512-a3Z3ytYl6R/+7ldxx04PO1semkwWlX/8pTqxsPw4quIcIXDFPZhOc1Wx8azWmkU26ccK3mHwcWenn0avNgAKQg== + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -3174,6 +3179,13 @@ "@types/connect" "*" "@types/node" "*" +"@types/bun@^1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@types/bun/-/bun-1.1.13.tgz#a53b3a64c8f539bbe33ceb0a9f3b98c6a79a3407" + integrity sha512-KmQxSBgVWCl6RSuerlLGZlIWfdxkKqat0nxN61+qu4y1KDn0Ll3j7v1Pl8GnaL3a/U6GGWVTJh75ap62kR1E8Q== + dependencies: + bun-types "1.1.34" + "@types/co-body@^6.1.3": version "6.1.3" resolved "https://registry.yarnpkg.com/@types/co-body/-/co-body-6.1.3.tgz#201796c6389066b400cfcb4e1ec5c3db798265a2" @@ -3499,6 +3511,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@~20.12.8": + version "20.12.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.14.tgz#0c5cf7ef26aedfd64b0539bba9380ed1f57dcc77" + integrity sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -3633,6 +3652,13 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== +"@types/ws@~8.5.10": + version "8.5.13" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" + integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -4603,6 +4629,14 @@ bullmq@^4.6.0: tslib "^2.0.0" uuid "^9.0.0" +bun-types@1.1.34: + version "1.1.34" + resolved "https://registry.yarnpkg.com/bun-types/-/bun-types-1.1.34.tgz#cf0e1dc5aa8875573a3acb09bead0f23bab5aca2" + integrity sha512-br5QygTEL/TwB4uQOb96Ky22j4Gq2WxWH/8Oqv20fk5HagwKXo/akB+LiYgSfzexCt6kkcUaVm+bKiPl71xPvw== + dependencies: + "@types/node" "~20.12.8" + "@types/ws" "~8.5.10" + bundle-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" @@ -5803,9 +5837,19 @@ electron-to-chromium@^1.5.28: integrity sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A== electron-to-chromium@^1.5.41: - version "1.5.56" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz#3213f369efc3a41091c3b2c05bc0f406108ac1df" - integrity sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw== + version "1.5.57" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.57.tgz#cb43af8784166bca24565b3418bf5f775a6b1c86" + integrity sha512-xS65H/tqgOwUBa5UmOuNSLuslDo7zho0y/lgQw35pnrqiZh7UOWHCeL/Bt6noJATbA6tpQJGCifsFsIRZj1Fqg== + +elysia@^1.1.24: + version "1.1.24" + resolved "https://registry.yarnpkg.com/elysia/-/elysia-1.1.24.tgz#093eeb60c8338e4e14bae33e3718b54682dbcabe" + integrity sha512-viJOb+PADrxJn7ntlQfoXsLuVgHNS09gn9dj2yroHnwqheN0hZp81WCGRQXkvh4kW5zGISX50/eqAcoNDpdL/g== + dependencies: + "@sinclair/typebox" "0.32.34" + cookie "^1.0.1" + fast-decode-uri-component "^1.0.1" + openapi-types "^12.1.3" emittery@^0.13.1: version "0.13.1" @@ -10113,6 +10157,11 @@ open@9.1.0: is-inside-container "^1.0.0" is-wsl "^2.2.0" +openapi-types@^12.1.3: + version "12.1.3" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" + integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"