feat: add initial API
This commit is contained in:
13
README.md
13
README.md
@@ -29,7 +29,6 @@ docker-compose up -d
|
|||||||
- **WebSocket MQTT**: `ws://localhost:8883/ws`
|
- **WebSocket MQTT**: `ws://localhost:8883/ws`
|
||||||
- **HTTP API**: `http://localhost:8883/api`
|
- **HTTP API**: `http://localhost:8883/api`
|
||||||
|
|
||||||
|
|
||||||
3. Connect with an MQTT client:
|
3. Connect with an MQTT client:
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
@@ -49,15 +48,16 @@ const client = mqtt.connect('ws://localhost:8883/ws')
|
|||||||
Backbone can be configured using environment variables:
|
Backbone can be configured using environment variables:
|
||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
|----------|-------------|---------|
|
| -------------------- | ---------------------------------------- | ----------- |
|
||||||
| `ADMIN_TOKEN` | Admin token for API requests | `undefined` |
|
| `ADMIN_TOKEN` | Admin token for API requests | `undefined` |
|
||||||
| `TOKEN_SECRET` | JWT signing secret for authentication | `undefined` |
|
| `JWT_SECRET` | JWT signing secret for authentication | `undefined` |
|
||||||
| `K8S_ENABLED` | Enable Kubernetes operator mode | `false` |
|
| `K8S_ENABLED` | Enable Kubernetes operator mode | `false` |
|
||||||
| `HTTP_ENABLED` | Enable HTTP/WebSocket server | `true` |
|
| `WS_ENABLED` | Enable WebSocket MQTT server | `false` |
|
||||||
|
| `API_ENABLED` | Enable HTTP API | `false` |
|
||||||
| `HTTP_PORT` | HTTP server port | `8883` |
|
| `HTTP_PORT` | HTTP server port | `8883` |
|
||||||
| `TCP_ENABLED` | Enable TCP MQTT server | `true` |
|
| `TCP_ENABLED` | Enable TCP MQTT server | `false` |
|
||||||
| `TCP_PORT` | TCP server port | `1883` |
|
| `TCP_PORT` | TCP server port | `1883` |
|
||||||
| `OIDC_ENABLED` | OIDC discovery URL | `undefined` |
|
| `OIDC_ENABLED` | OIDC discovery URL | `false` |
|
||||||
| `OIDC_DISCOVERY` | OIDC discovery URL | `undefined` |
|
| `OIDC_DISCOVERY` | OIDC discovery URL | `undefined` |
|
||||||
| `OIDC_CLIENT_ID` | OIDC client ID | `undefined` |
|
| `OIDC_CLIENT_ID` | OIDC client ID | `undefined` |
|
||||||
| `OIDC_CLIENT_SECRET` | OIDC client secret | `undefined` |
|
| `OIDC_CLIENT_SECRET` | OIDC client secret | `undefined` |
|
||||||
@@ -159,6 +159,7 @@ statements:
|
|||||||
### HTTP API
|
### HTTP API
|
||||||
|
|
||||||
The HTTP API provides management endpoints for:
|
The HTTP API provides management endpoints for:
|
||||||
|
|
||||||
- Client management
|
- Client management
|
||||||
- Topic configuration
|
- Topic configuration
|
||||||
- Broker statistics
|
- Broker statistics
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/exports.js",
|
"main": "dist/exports.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node --no-warnings --watch src/start.ts",
|
"dev": "node --no-warnings --watch src/dev.ts",
|
||||||
"test:lint": "eslint",
|
"test:lint": "eslint",
|
||||||
"build": "tsc --build",
|
"build": "tsc --build",
|
||||||
"test:unit": "vitest --run --passWithNoTests",
|
"test:unit": "vitest --run --passWithNoTests",
|
||||||
@@ -41,13 +41,18 @@
|
|||||||
"#root/*": "./src/*"
|
"#root/*": "./src/*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fastify/sensible": "^6.0.3",
|
||||||
|
"@fastify/swagger": "^9.5.2",
|
||||||
"@fastify/websocket": "^11.2.0",
|
"@fastify/websocket": "^11.2.0",
|
||||||
"@kubernetes/client-node": "^1.4.0",
|
"@kubernetes/client-node": "^1.4.0",
|
||||||
|
"@scalar/fastify-api-reference": "^1.38.1",
|
||||||
"aedes": "^0.51.3",
|
"aedes": "^0.51.3",
|
||||||
|
"aedes-packet": "^3.0.0",
|
||||||
"aedes-persistence": "^10.2.2",
|
"aedes-persistence": "^10.2.2",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"better-sqlite3": "^12.4.1",
|
"better-sqlite3": "^12.4.1",
|
||||||
"fastify": "^5.6.1",
|
"fastify": "^5.6.1",
|
||||||
|
"fastify-type-provider-zod": "^6.0.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"knex": "^3.1.0",
|
"knex": "^3.1.0",
|
||||||
"micromatch": "^4.0.8",
|
"micromatch": "^4.0.8",
|
||||||
|
|||||||
321
pnpm-lock.yaml
generated
321
pnpm-lock.yaml
generated
@@ -8,15 +8,27 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fastify/sensible':
|
||||||
|
specifier: ^6.0.3
|
||||||
|
version: 6.0.3
|
||||||
|
'@fastify/swagger':
|
||||||
|
specifier: ^9.5.2
|
||||||
|
version: 9.5.2
|
||||||
'@fastify/websocket':
|
'@fastify/websocket':
|
||||||
specifier: ^11.2.0
|
specifier: ^11.2.0
|
||||||
version: 11.2.0
|
version: 11.2.0
|
||||||
'@kubernetes/client-node':
|
'@kubernetes/client-node':
|
||||||
specifier: ^1.4.0
|
specifier: ^1.4.0
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
|
'@scalar/fastify-api-reference':
|
||||||
|
specifier: ^1.38.1
|
||||||
|
version: 1.38.1
|
||||||
aedes:
|
aedes:
|
||||||
specifier: ^0.51.3
|
specifier: ^0.51.3
|
||||||
version: 0.51.3
|
version: 0.51.3
|
||||||
|
aedes-packet:
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
aedes-persistence:
|
aedes-persistence:
|
||||||
specifier: ^10.2.2
|
specifier: ^10.2.2
|
||||||
version: 10.2.2
|
version: 10.2.2
|
||||||
@@ -29,6 +41,9 @@ importers:
|
|||||||
fastify:
|
fastify:
|
||||||
specifier: ^5.6.1
|
specifier: ^5.6.1
|
||||||
version: 5.6.1
|
version: 5.6.1
|
||||||
|
fastify-type-provider-zod:
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.0.0(@fastify/swagger@9.5.2)(fastify@5.6.1)(openapi-types@12.1.3)(zod@4.1.12)
|
||||||
jsonwebtoken:
|
jsonwebtoken:
|
||||||
specifier: ^9.0.2
|
specifier: ^9.0.2
|
||||||
version: 9.0.2
|
version: 9.0.2
|
||||||
@@ -71,7 +86,7 @@ importers:
|
|||||||
version: 8.18.1
|
version: 8.18.1
|
||||||
'@vitest/coverage-v8':
|
'@vitest/coverage-v8':
|
||||||
specifier: 3.2.4
|
specifier: 3.2.4
|
||||||
version: 3.2.4(vitest@3.2.4(@types/node@24.7.2))
|
version: 3.2.4(vitest@3.2.4(@types/node@24.7.2)(yaml@2.8.1))
|
||||||
eslint:
|
eslint:
|
||||||
specifier: 9.37.0
|
specifier: 9.37.0
|
||||||
version: 9.37.0
|
version: 9.37.0
|
||||||
@@ -101,7 +116,7 @@ importers:
|
|||||||
version: 8.46.1(eslint@9.37.0)(typescript@5.9.3)
|
version: 8.46.1(eslint@9.37.0)(typescript@5.9.3)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: 3.2.4
|
specifier: 3.2.4
|
||||||
version: 3.2.4(@types/node@24.7.2)
|
version: 3.2.4(@types/node@24.7.2)(yaml@2.8.1)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -350,6 +365,12 @@ packages:
|
|||||||
'@fastify/proxy-addr@5.1.0':
|
'@fastify/proxy-addr@5.1.0':
|
||||||
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
||||||
|
|
||||||
|
'@fastify/sensible@6.0.3':
|
||||||
|
resolution: {integrity: sha512-Iyn8698hp/e5+v8SNBBruTa7UfrMEP52R16dc9jMpqSyEcPsvWFQo+R6WwHCUnJiLIsuci2ZoEZ7ilrSSCPIVg==}
|
||||||
|
|
||||||
|
'@fastify/swagger@9.5.2':
|
||||||
|
resolution: {integrity: sha512-8e8w/LItg/cF6IR/hYKtnt+E0QImees5o3YWJsTLxaIk+tzNUEc6Z2Ursi4oOHWwUlKjUCnV6yh5z5ZdxvlsWA==}
|
||||||
|
|
||||||
'@fastify/websocket@11.2.0':
|
'@fastify/websocket@11.2.0':
|
||||||
resolution: {integrity: sha512-3HrDPbAG1CzUCqnslgJxppvzaAZffieOVbLp1DAy1huCSynUWPifSvfdEDUR8HlJLp3sp1A36uOM2tJogADS8w==}
|
resolution: {integrity: sha512-3HrDPbAG1CzUCqnslgJxppvzaAZffieOVbLp1DAy1huCSynUWPifSvfdEDUR8HlJLp3sp1A36uOM2tJogADS8w==}
|
||||||
|
|
||||||
@@ -409,6 +430,10 @@ packages:
|
|||||||
'@kubernetes/client-node@1.4.0':
|
'@kubernetes/client-node@1.4.0':
|
||||||
resolution: {integrity: sha512-Zge3YvF7DJi264dU1b3wb/GmzR99JhUpqTvp+VGHfwZT+g7EOOYNScDJNZwXy9cszyIGPIs0VHr+kk8e95qqrA==}
|
resolution: {integrity: sha512-Zge3YvF7DJi264dU1b3wb/GmzR99JhUpqTvp+VGHfwZT+g7EOOYNScDJNZwXy9cszyIGPIs0VHr+kk8e95qqrA==}
|
||||||
|
|
||||||
|
'@lukeed/ms@2.0.2':
|
||||||
|
resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -683,6 +708,38 @@ packages:
|
|||||||
'@rtsao/scc@1.1.0':
|
'@rtsao/scc@1.1.0':
|
||||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||||
|
|
||||||
|
'@scalar/core@0.3.20':
|
||||||
|
resolution: {integrity: sha512-bIlrePx41pSvjDcaJPa9YVVhbSm0N9SKQm2Fzl489S0bUVToyXIQtMFVR4i+BmXGjOcATm/66ELW4vdXRjHoRA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/fastify-api-reference@1.38.1':
|
||||||
|
resolution: {integrity: sha512-olRjyMn45gTB1tYCXjjQMhAeku8Z46rlPNYYAg9BU+dcfcm1miuxT0cYhB2U9pI5rZtoh2j305M7FG7ShlY2hQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/helpers@0.0.12':
|
||||||
|
resolution: {integrity: sha512-4NDmHShyi1hrVRsJCdRZT/FIpy+/5PFbVbQLRYX/pjpu5cYqHBj9s6n5RI6gGDXEBHAIFi63g9FC6Isgr66l1Q==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/json-magic@0.6.1':
|
||||||
|
resolution: {integrity: sha512-HJMPY5dUU3EXVS4EkjAFXo+uCrby/YFu/gljKDQnhYWRy5zQ0sJWrOEDcHS8nLoJRCIRD5tiVpCxnUnSb6OoAQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/openapi-parser@0.22.3':
|
||||||
|
resolution: {integrity: sha512-5Znbx9HVJb7EV9EJXJrUj+cs116QIBwX/hxkyaiLaaDL2w5S+z1rjtV+d0Jv7382FCtzAtfv/9llVuxZYPVqXA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/openapi-types@0.5.0':
|
||||||
|
resolution: {integrity: sha512-HJBcLa+/mPP+3TCcQngj/iW5UqynRosOQdEETXjmdy6Ngw8wBjwIcT6C86J5jufJ6sI8++HYnt+e7pAvp5FO6A==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/openapi-upgrader@0.1.3':
|
||||||
|
resolution: {integrity: sha512-iROhcgy3vge6zsviPtoTLHale0nYt1PLhuMmJweQwJ0U23ZYyYhV5xgHtAd0OBEXuqT6rjYbJFvKOJZmJOwpNQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@scalar/types@0.3.2':
|
||||||
|
resolution: {integrity: sha512-+X10CCvG57nAqYbTGteiSzRFQcMYm7DLfCRMeEfiWQ9Bq2ladat17XsMSvkvwcfpOSlsoepWf3P5dErERUSOQQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@types/braces@3.0.5':
|
'@types/braces@3.0.5':
|
||||||
resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==}
|
resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==}
|
||||||
|
|
||||||
@@ -870,6 +927,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
ajv-draft-04@1.0.0:
|
||||||
|
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.5.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
|
||||||
ajv-formats@3.0.1:
|
ajv-formats@3.0.1:
|
||||||
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
|
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1253,6 +1318,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
|
depd@2.0.0:
|
||||||
|
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
dequal@2.0.3:
|
dequal@2.0.3:
|
||||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -1506,9 +1575,20 @@ packages:
|
|||||||
resolution: {integrity: sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==}
|
resolution: {integrity: sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
fastify-plugin@4.5.1:
|
||||||
|
resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==}
|
||||||
|
|
||||||
fastify-plugin@5.1.0:
|
fastify-plugin@5.1.0:
|
||||||
resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==}
|
resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==}
|
||||||
|
|
||||||
|
fastify-type-provider-zod@6.0.0:
|
||||||
|
resolution: {integrity: sha512-Bz+Qll2XuvvueHz0yhcr67V/43q1VecSyIqZm+P8OL8KZHznUXECZXkuwQePR5b6fWY/kzhhadmgNs9dB/Nifg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@fastify/swagger': '>=9.5.1'
|
||||||
|
fastify: ^5.0.0
|
||||||
|
openapi-types: ^12.1.3
|
||||||
|
zod: '>=4.1.5'
|
||||||
|
|
||||||
fastify@5.6.1:
|
fastify@5.6.1:
|
||||||
resolution: {integrity: sha512-WjjlOciBF0K8pDUPZoGPhqhKrQJ02I8DKaDIfO51EL0kbSMwQFl85cRwhOvmSDWoukNOdTo27gLN549pLCcH7Q==}
|
resolution: {integrity: sha512-WjjlOciBF0K8pDUPZoGPhqhKrQJ02I8DKaDIfO51EL0kbSMwQFl85cRwhOvmSDWoukNOdTo27gLN549pLCcH7Q==}
|
||||||
|
|
||||||
@@ -1568,6 +1648,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
|
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
forwarded@0.2.0:
|
||||||
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
fs-constants@1.0.0:
|
fs-constants@1.0.0:
|
||||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
|
|
||||||
@@ -1623,6 +1707,9 @@ packages:
|
|||||||
github-from-package@0.0.0:
|
github-from-package@0.0.0:
|
||||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||||
|
|
||||||
|
github-slugger@2.0.0:
|
||||||
|
resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -1693,6 +1780,10 @@ packages:
|
|||||||
html-escaper@2.0.2:
|
html-escaper@2.0.2:
|
||||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||||
|
|
||||||
|
http-errors@2.0.0:
|
||||||
|
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
human-signals@2.1.0:
|
human-signals@2.1.0:
|
||||||
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
|
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
|
||||||
engines: {node: '>=10.17.0'}
|
engines: {node: '>=10.17.0'}
|
||||||
@@ -1930,6 +2021,10 @@ packages:
|
|||||||
json-schema-ref-resolver@3.0.0:
|
json-schema-ref-resolver@3.0.0:
|
||||||
resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
|
resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
|
||||||
|
|
||||||
|
json-schema-resolver@3.0.0:
|
||||||
|
resolution: {integrity: sha512-HqMnbz0tz2DaEJ3ntsqtx3ezzZyDE7G56A/pPY/NGmrPu76UzsWquOpHFRAf5beTNXoH2LU5cQePVvRli1nchA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
json-schema-traverse@0.4.1:
|
json-schema-traverse@0.4.1:
|
||||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||||
|
|
||||||
@@ -1956,6 +2051,10 @@ packages:
|
|||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
jsonpointer@5.0.1:
|
||||||
|
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
jsonwebtoken@9.0.2:
|
jsonwebtoken@9.0.2:
|
||||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
||||||
engines: {node: '>=12', npm: '>=6'}
|
engines: {node: '>=12', npm: '>=6'}
|
||||||
@@ -1997,6 +2096,10 @@ packages:
|
|||||||
tedious:
|
tedious:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
leven@4.1.0:
|
||||||
|
resolution: {integrity: sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -2070,6 +2173,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
media-typer@0.3.0:
|
||||||
|
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
mem@8.1.1:
|
mem@8.1.1:
|
||||||
resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==}
|
resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2148,6 +2255,11 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
nanoid@5.1.5:
|
||||||
|
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
|
||||||
|
engines: {node: ^18 || >=20}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
napi-build-utils@2.0.0:
|
napi-build-utils@2.0.0:
|
||||||
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||||
|
|
||||||
@@ -2224,6 +2336,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
openapi-types@12.1.3:
|
||||||
|
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
||||||
|
|
||||||
openid-client@6.8.1:
|
openid-client@6.8.1:
|
||||||
resolution: {integrity: sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==}
|
resolution: {integrity: sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==}
|
||||||
|
|
||||||
@@ -2598,6 +2713,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
setprototypeof@1.2.0:
|
||||||
|
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2681,6 +2799,10 @@ packages:
|
|||||||
stacktracey@2.1.8:
|
stacktracey@2.1.8:
|
||||||
resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==}
|
resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==}
|
||||||
|
|
||||||
|
statuses@2.0.1:
|
||||||
|
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
std-env@3.10.0:
|
std-env@3.10.0:
|
||||||
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||||
|
|
||||||
@@ -2771,6 +2893,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
|
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
|
|
||||||
|
tagged-tag@1.0.0:
|
||||||
|
resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
tar-fs@2.1.4:
|
tar-fs@2.1.4:
|
||||||
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
|
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
|
||||||
|
|
||||||
@@ -2835,6 +2961,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
|
resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
toidentifier@1.0.1:
|
||||||
|
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||||
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
tr46@0.0.3:
|
tr46@0.0.3:
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
|
||||||
@@ -2865,6 +2995,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
|
resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
type-fest@5.0.0:
|
||||||
|
resolution: {integrity: sha512-GeJop7+u7BYlQ6yQCAY1nBQiRSHR+6OdCEtd8Bwp9a3NK3+fWAVjOaPKJDteB9f6cIJ0wt4IfnScjLG450EpXA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
type-is@1.6.18:
|
||||||
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
typed-array-buffer@1.0.3:
|
typed-array-buffer@1.0.3:
|
||||||
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2924,6 +3062,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
vary@1.1.2:
|
||||||
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
vite-node@3.2.4:
|
vite-node@3.2.4:
|
||||||
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
|
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
|
||||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
@@ -3101,10 +3243,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
engines: {node: '>=0.4'}
|
engines: {node: '>=0.4'}
|
||||||
|
|
||||||
|
yaml@2.8.0:
|
||||||
|
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
|
||||||
|
engines: {node: '>= 14.6'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
yaml@2.8.1:
|
||||||
|
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
|
||||||
|
engines: {node: '>= 14.6'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
yocto-queue@0.1.0:
|
yocto-queue@0.1.0:
|
||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
zod@4.1.11:
|
||||||
|
resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==}
|
||||||
|
|
||||||
zod@4.1.12:
|
zod@4.1.12:
|
||||||
resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==}
|
resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==}
|
||||||
|
|
||||||
@@ -3285,6 +3440,26 @@ snapshots:
|
|||||||
'@fastify/forwarded': 3.0.1
|
'@fastify/forwarded': 3.0.1
|
||||||
ipaddr.js: 2.2.0
|
ipaddr.js: 2.2.0
|
||||||
|
|
||||||
|
'@fastify/sensible@6.0.3':
|
||||||
|
dependencies:
|
||||||
|
'@lukeed/ms': 2.0.2
|
||||||
|
dequal: 2.0.3
|
||||||
|
fastify-plugin: 5.1.0
|
||||||
|
forwarded: 0.2.0
|
||||||
|
http-errors: 2.0.0
|
||||||
|
type-is: 1.6.18
|
||||||
|
vary: 1.1.2
|
||||||
|
|
||||||
|
'@fastify/swagger@9.5.2':
|
||||||
|
dependencies:
|
||||||
|
fastify-plugin: 5.1.0
|
||||||
|
json-schema-resolver: 3.0.0
|
||||||
|
openapi-types: 12.1.3
|
||||||
|
rfdc: 1.4.1
|
||||||
|
yaml: 2.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@fastify/websocket@11.2.0':
|
'@fastify/websocket@11.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
duplexify: 4.1.3
|
duplexify: 4.1.3
|
||||||
@@ -3367,6 +3542,8 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
|
'@lukeed/ms@2.0.2': {}
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@@ -3686,6 +3863,52 @@ snapshots:
|
|||||||
|
|
||||||
'@rtsao/scc@1.1.0': {}
|
'@rtsao/scc@1.1.0': {}
|
||||||
|
|
||||||
|
'@scalar/core@0.3.20':
|
||||||
|
dependencies:
|
||||||
|
'@scalar/types': 0.3.2
|
||||||
|
|
||||||
|
'@scalar/fastify-api-reference@1.38.1':
|
||||||
|
dependencies:
|
||||||
|
'@scalar/core': 0.3.20
|
||||||
|
'@scalar/openapi-parser': 0.22.3
|
||||||
|
'@scalar/openapi-types': 0.5.0
|
||||||
|
fastify-plugin: 4.5.1
|
||||||
|
github-slugger: 2.0.0
|
||||||
|
|
||||||
|
'@scalar/helpers@0.0.12': {}
|
||||||
|
|
||||||
|
'@scalar/json-magic@0.6.1':
|
||||||
|
dependencies:
|
||||||
|
'@scalar/helpers': 0.0.12
|
||||||
|
yaml: 2.8.0
|
||||||
|
|
||||||
|
'@scalar/openapi-parser@0.22.3':
|
||||||
|
dependencies:
|
||||||
|
'@scalar/json-magic': 0.6.1
|
||||||
|
'@scalar/openapi-types': 0.5.0
|
||||||
|
'@scalar/openapi-upgrader': 0.1.3
|
||||||
|
ajv: 8.17.1
|
||||||
|
ajv-draft-04: 1.0.0(ajv@8.17.1)
|
||||||
|
ajv-formats: 3.0.1(ajv@8.17.1)
|
||||||
|
jsonpointer: 5.0.1
|
||||||
|
leven: 4.1.0
|
||||||
|
yaml: 2.8.0
|
||||||
|
|
||||||
|
'@scalar/openapi-types@0.5.0':
|
||||||
|
dependencies:
|
||||||
|
zod: 4.1.11
|
||||||
|
|
||||||
|
'@scalar/openapi-upgrader@0.1.3':
|
||||||
|
dependencies:
|
||||||
|
'@scalar/openapi-types': 0.5.0
|
||||||
|
|
||||||
|
'@scalar/types@0.3.2':
|
||||||
|
dependencies:
|
||||||
|
'@scalar/openapi-types': 0.5.0
|
||||||
|
nanoid: 5.1.5
|
||||||
|
type-fest: 5.0.0
|
||||||
|
zod: 4.1.11
|
||||||
|
|
||||||
'@types/braces@3.0.5': {}
|
'@types/braces@3.0.5': {}
|
||||||
|
|
||||||
'@types/chai@5.2.2':
|
'@types/chai@5.2.2':
|
||||||
@@ -3831,7 +4054,7 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.46.1
|
'@typescript-eslint/types': 8.46.1
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.7.2))':
|
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.7.2)(yaml@2.8.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
'@bcoe/v8-coverage': 1.0.2
|
'@bcoe/v8-coverage': 1.0.2
|
||||||
@@ -3846,7 +4069,7 @@ snapshots:
|
|||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
test-exclude: 7.0.1
|
test-exclude: 7.0.1
|
||||||
tinyrainbow: 2.0.0
|
tinyrainbow: 2.0.0
|
||||||
vitest: 3.2.4(@types/node@24.7.2)
|
vitest: 3.2.4(@types/node@24.7.2)(yaml@2.8.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -3858,13 +4081,13 @@ snapshots:
|
|||||||
chai: 5.3.3
|
chai: 5.3.3
|
||||||
tinyrainbow: 2.0.0
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
'@vitest/mocker@3.2.4(vite@7.1.10(@types/node@24.7.2))':
|
'@vitest/mocker@3.2.4(vite@7.1.10(@types/node@24.7.2)(yaml@2.8.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/spy': 3.2.4
|
'@vitest/spy': 3.2.4
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
magic-string: 0.30.19
|
magic-string: 0.30.19
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 7.1.10(@types/node@24.7.2)
|
vite: 7.1.10(@types/node@24.7.2)(yaml@2.8.1)
|
||||||
|
|
||||||
'@vitest/pretty-format@3.2.4':
|
'@vitest/pretty-format@3.2.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3947,6 +4170,10 @@ snapshots:
|
|||||||
|
|
||||||
agent-base@7.1.4: {}
|
agent-base@7.1.4: {}
|
||||||
|
|
||||||
|
ajv-draft-04@1.0.0(ajv@8.17.1):
|
||||||
|
optionalDependencies:
|
||||||
|
ajv: 8.17.1
|
||||||
|
|
||||||
ajv-formats@3.0.1(ajv@8.17.1):
|
ajv-formats@3.0.1(ajv@8.17.1):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
ajv: 8.17.1
|
ajv: 8.17.1
|
||||||
@@ -4353,6 +4580,8 @@ snapshots:
|
|||||||
|
|
||||||
delayed-stream@1.0.0: {}
|
delayed-stream@1.0.0: {}
|
||||||
|
|
||||||
|
depd@2.0.0: {}
|
||||||
|
|
||||||
dequal@2.0.3: {}
|
dequal@2.0.3: {}
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
detect-libc@2.1.2: {}
|
||||||
@@ -4719,8 +4948,18 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
reusify: 1.1.0
|
reusify: 1.1.0
|
||||||
|
|
||||||
|
fastify-plugin@4.5.1: {}
|
||||||
|
|
||||||
fastify-plugin@5.1.0: {}
|
fastify-plugin@5.1.0: {}
|
||||||
|
|
||||||
|
fastify-type-provider-zod@6.0.0(@fastify/swagger@9.5.2)(fastify@5.6.1)(openapi-types@12.1.3)(zod@4.1.12):
|
||||||
|
dependencies:
|
||||||
|
'@fastify/error': 4.2.0
|
||||||
|
'@fastify/swagger': 9.5.2
|
||||||
|
fastify: 5.6.1
|
||||||
|
openapi-types: 12.1.3
|
||||||
|
zod: 4.1.12
|
||||||
|
|
||||||
fastify@5.6.1:
|
fastify@5.6.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/ajv-compiler': 4.0.3
|
'@fastify/ajv-compiler': 4.0.3
|
||||||
@@ -4799,6 +5038,8 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
|
forwarded@0.2.0: {}
|
||||||
|
|
||||||
fs-constants@1.0.0: {}
|
fs-constants@1.0.0: {}
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
@@ -4858,6 +5099,8 @@ snapshots:
|
|||||||
|
|
||||||
github-from-package@0.0.0: {}
|
github-from-package@0.0.0: {}
|
||||||
|
|
||||||
|
github-slugger@2.0.0: {}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -4918,6 +5161,14 @@ snapshots:
|
|||||||
|
|
||||||
html-escaper@2.0.2: {}
|
html-escaper@2.0.2: {}
|
||||||
|
|
||||||
|
http-errors@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
depd: 2.0.0
|
||||||
|
inherits: 2.0.4
|
||||||
|
setprototypeof: 1.2.0
|
||||||
|
statuses: 2.0.1
|
||||||
|
toidentifier: 1.0.1
|
||||||
|
|
||||||
human-signals@2.1.0: {}
|
human-signals@2.1.0: {}
|
||||||
|
|
||||||
hyperid@3.3.0:
|
hyperid@3.3.0:
|
||||||
@@ -5138,6 +5389,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
|
|
||||||
|
json-schema-resolver@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
fast-uri: 3.1.0
|
||||||
|
rfdc: 1.4.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
json-schema-traverse@0.4.1: {}
|
json-schema-traverse@0.4.1: {}
|
||||||
|
|
||||||
json-schema-traverse@1.0.0: {}
|
json-schema-traverse@1.0.0: {}
|
||||||
@@ -5158,6 +5417,8 @@ snapshots:
|
|||||||
'@jsep-plugin/regex': 1.0.4(jsep@1.4.0)
|
'@jsep-plugin/regex': 1.0.4(jsep@1.4.0)
|
||||||
jsep: 1.4.0
|
jsep: 1.4.0
|
||||||
|
|
||||||
|
jsonpointer@5.0.1: {}
|
||||||
|
|
||||||
jsonwebtoken@9.0.2:
|
jsonwebtoken@9.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
jws: 3.2.2
|
jws: 3.2.2
|
||||||
@@ -5208,6 +5469,8 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
leven@4.1.0: {}
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
@@ -5276,6 +5539,8 @@ snapshots:
|
|||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
media-typer@0.3.0: {}
|
||||||
|
|
||||||
mem@8.1.1:
|
mem@8.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
map-age-cleaner: 0.1.3
|
map-age-cleaner: 0.1.3
|
||||||
@@ -5366,6 +5631,8 @@ snapshots:
|
|||||||
|
|
||||||
nanoid@3.3.11: {}
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
|
nanoid@5.1.5: {}
|
||||||
|
|
||||||
napi-build-utils@2.0.0: {}
|
napi-build-utils@2.0.0: {}
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
@@ -5446,6 +5713,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn: 2.1.0
|
mimic-fn: 2.1.0
|
||||||
|
|
||||||
|
openapi-types@12.1.3: {}
|
||||||
|
|
||||||
openid-client@6.8.1:
|
openid-client@6.8.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
jose: 6.1.0
|
jose: 6.1.0
|
||||||
@@ -5843,6 +6112,8 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
|
setprototypeof@1.2.0: {}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
shebang-regex: 3.0.0
|
shebang-regex: 3.0.0
|
||||||
@@ -5933,6 +6204,8 @@ snapshots:
|
|||||||
as-table: 1.0.55
|
as-table: 1.0.55
|
||||||
get-source: 2.0.12
|
get-source: 2.0.12
|
||||||
|
|
||||||
|
statuses@2.0.1: {}
|
||||||
|
|
||||||
std-env@3.10.0: {}
|
std-env@3.10.0: {}
|
||||||
|
|
||||||
stop-iteration-iterator@1.1.0:
|
stop-iteration-iterator@1.1.0:
|
||||||
@@ -6031,6 +6304,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@pkgr/core': 0.2.9
|
'@pkgr/core': 0.2.9
|
||||||
|
|
||||||
|
tagged-tag@1.0.0: {}
|
||||||
|
|
||||||
tar-fs@2.1.4:
|
tar-fs@2.1.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr: 1.1.4
|
chownr: 1.1.4
|
||||||
@@ -6112,6 +6387,8 @@ snapshots:
|
|||||||
|
|
||||||
toad-cache@3.7.0: {}
|
toad-cache@3.7.0: {}
|
||||||
|
|
||||||
|
toidentifier@1.0.1: {}
|
||||||
|
|
||||||
tr46@0.0.3: {}
|
tr46@0.0.3: {}
|
||||||
|
|
||||||
ts-api-utils@2.1.0(typescript@5.9.3):
|
ts-api-utils@2.1.0(typescript@5.9.3):
|
||||||
@@ -6139,6 +6416,15 @@ snapshots:
|
|||||||
|
|
||||||
type-fest@0.6.0: {}
|
type-fest@0.6.0: {}
|
||||||
|
|
||||||
|
type-fest@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
tagged-tag: 1.0.0
|
||||||
|
|
||||||
|
type-is@1.6.18:
|
||||||
|
dependencies:
|
||||||
|
media-typer: 0.3.0
|
||||||
|
mime-types: 2.1.35
|
||||||
|
|
||||||
typed-array-buffer@1.0.3:
|
typed-array-buffer@1.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
@@ -6212,13 +6498,15 @@ snapshots:
|
|||||||
|
|
||||||
uuid@8.3.2: {}
|
uuid@8.3.2: {}
|
||||||
|
|
||||||
vite-node@3.2.4(@types/node@24.7.2):
|
vary@1.1.2: {}
|
||||||
|
|
||||||
|
vite-node@3.2.4(@types/node@24.7.2)(yaml@2.8.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
es-module-lexer: 1.7.0
|
es-module-lexer: 1.7.0
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
vite: 7.1.10(@types/node@24.7.2)
|
vite: 7.1.10(@types/node@24.7.2)(yaml@2.8.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- jiti
|
- jiti
|
||||||
@@ -6233,7 +6521,7 @@ snapshots:
|
|||||||
- tsx
|
- tsx
|
||||||
- yaml
|
- yaml
|
||||||
|
|
||||||
vite@7.1.10(@types/node@24.7.2):
|
vite@7.1.10(@types/node@24.7.2)(yaml@2.8.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.25.10
|
esbuild: 0.25.10
|
||||||
fdir: 6.5.0(picomatch@4.0.3)
|
fdir: 6.5.0(picomatch@4.0.3)
|
||||||
@@ -6244,12 +6532,13 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 24.7.2
|
'@types/node': 24.7.2
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
yaml: 2.8.1
|
||||||
|
|
||||||
vitest@3.2.4(@types/node@24.7.2):
|
vitest@3.2.4(@types/node@24.7.2)(yaml@2.8.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/chai': 5.2.2
|
'@types/chai': 5.2.2
|
||||||
'@vitest/expect': 3.2.4
|
'@vitest/expect': 3.2.4
|
||||||
'@vitest/mocker': 3.2.4(vite@7.1.10(@types/node@24.7.2))
|
'@vitest/mocker': 3.2.4(vite@7.1.10(@types/node@24.7.2)(yaml@2.8.1))
|
||||||
'@vitest/pretty-format': 3.2.4
|
'@vitest/pretty-format': 3.2.4
|
||||||
'@vitest/runner': 3.2.4
|
'@vitest/runner': 3.2.4
|
||||||
'@vitest/snapshot': 3.2.4
|
'@vitest/snapshot': 3.2.4
|
||||||
@@ -6267,8 +6556,8 @@ snapshots:
|
|||||||
tinyglobby: 0.2.15
|
tinyglobby: 0.2.15
|
||||||
tinypool: 1.1.1
|
tinypool: 1.1.1
|
||||||
tinyrainbow: 2.0.0
|
tinyrainbow: 2.0.0
|
||||||
vite: 7.1.10(@types/node@24.7.2)
|
vite: 7.1.10(@types/node@24.7.2)(yaml@2.8.1)
|
||||||
vite-node: 3.2.4(@types/node@24.7.2)
|
vite-node: 3.2.4(@types/node@24.7.2)(yaml@2.8.1)
|
||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 24.7.2
|
'@types/node': 24.7.2
|
||||||
@@ -6431,6 +6720,12 @@ snapshots:
|
|||||||
|
|
||||||
xtend@4.0.2: {}
|
xtend@4.0.2: {}
|
||||||
|
|
||||||
|
yaml@2.8.0: {}
|
||||||
|
|
||||||
|
yaml@2.8.1: {}
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
zod@4.1.11: {}
|
||||||
|
|
||||||
zod@4.1.12: {}
|
zod@4.1.12: {}
|
||||||
|
|||||||
@@ -1,8 +1,34 @@
|
|||||||
import { type FastifyPluginAsync } from 'fastify';
|
import { type FastifyPluginAsync } from 'fastify';
|
||||||
|
import { manageEndpoints } from './endpoints/endpoints.manage.ts';
|
||||||
|
import { authPlugin } from './plugins/plugins.auth.ts';
|
||||||
|
import { messageEndpoints } from './endpoints/endpoints.message.ts';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
const api: FastifyPluginAsync = async (fastify) => {
|
const api: FastifyPluginAsync = async (fastify) => {
|
||||||
fastify.get('/healthz', () => {
|
fastify.route({
|
||||||
|
method: 'get',
|
||||||
|
url: '/health',
|
||||||
|
schema: {
|
||||||
|
operationId: 'health.get',
|
||||||
|
summary: 'Get health status',
|
||||||
|
tags: ['system'],
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
status: z.literal('ok'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: () => {
|
||||||
return { status: 'ok' };
|
return { status: 'ok' };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await authPlugin(fastify, {});
|
||||||
|
|
||||||
|
await fastify.register(manageEndpoints, {
|
||||||
|
prefix: '/manage',
|
||||||
|
});
|
||||||
|
await fastify.register(messageEndpoints, {
|
||||||
|
prefix: '/message',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
45
src/api/endpoints/endpoints.manage.ts
Normal file
45
src/api/endpoints/endpoints.manage.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { JwtAuth } from '#root/auth/auth.jwt.ts';
|
||||||
|
import { statementSchema } from '#root/auth/auth.schemas.ts';
|
||||||
|
import { Config } from '#root/config/config.ts';
|
||||||
|
import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const manageEndpoints: FastifyPluginAsyncZod = async (fastify) => {
|
||||||
|
const config = fastify.services.get(Config);
|
||||||
|
|
||||||
|
if (config.jwtSecret) {
|
||||||
|
fastify.route({
|
||||||
|
method: 'post',
|
||||||
|
url: '/jwt',
|
||||||
|
schema: {
|
||||||
|
operationId: 'manage.jwt.post',
|
||||||
|
summary: 'Generate a JWT',
|
||||||
|
tags: ['manage'],
|
||||||
|
body: z.object({
|
||||||
|
exp: z.number().optional(),
|
||||||
|
statements: z.array(statementSchema),
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
jwt: z.string(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (req, reply) => {
|
||||||
|
if (
|
||||||
|
!req.session.validate({
|
||||||
|
action: 'mgmt:generate-jwt',
|
||||||
|
resource: 'mgmt/',
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
throw reply.unauthorized('not allowed');
|
||||||
|
}
|
||||||
|
const jwtAuth = fastify.services.get(JwtAuth);
|
||||||
|
const jwt = jwtAuth.generate(req.body);
|
||||||
|
reply.send({ jwt });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { manageEndpoints };
|
||||||
62
src/api/endpoints/endpoints.message.ts
Normal file
62
src/api/endpoints/endpoints.message.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Config } from '#root/config/config.ts';
|
||||||
|
import { MqttServer } from '#root/server/server.ts';
|
||||||
|
import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const messageEndpoints: FastifyPluginAsyncZod = async (fastify) => {
|
||||||
|
const config = fastify.services.get(Config);
|
||||||
|
|
||||||
|
if (config.jwtSecret) {
|
||||||
|
fastify.route({
|
||||||
|
method: 'post',
|
||||||
|
url: '',
|
||||||
|
schema: {
|
||||||
|
summary: 'Post a message to the bus',
|
||||||
|
operationId: 'message.post',
|
||||||
|
tags: ['message'],
|
||||||
|
body: z.object({
|
||||||
|
topic: z.string(),
|
||||||
|
dup: z.boolean(),
|
||||||
|
qos: z.union([z.literal(0), z.literal(1), z.literal(2)]),
|
||||||
|
retain: z.boolean(),
|
||||||
|
payload: z.string(),
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
success: z.literal(true),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (req, reply) => {
|
||||||
|
if (
|
||||||
|
!req.session.validate({
|
||||||
|
action: 'mqtt:publish',
|
||||||
|
resource: 'mgmt:',
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
throw reply.unauthorized('not allowed');
|
||||||
|
}
|
||||||
|
const server = fastify.services.get(MqttServer);
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
server.bus.publish(
|
||||||
|
{
|
||||||
|
...req.body,
|
||||||
|
cmd: 'publish',
|
||||||
|
payload: Buffer.from(req.body.payload, 'base64'),
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
reply.send({ success: true });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { messageEndpoints };
|
||||||
14
src/api/extensions.d.ts
vendored
Normal file
14
src/api/extensions.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { Session } from '#root/services/sessions/sessions.session.ts';
|
||||||
|
import type { Services } from '#root/utils/services.ts';
|
||||||
|
import 'fastify';
|
||||||
|
declare module 'fastify' {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export interface FastifyInstance {
|
||||||
|
services: Services;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export interface FastifyRequest {
|
||||||
|
session: Session;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/api/plugins/plugins.auth.ts
Normal file
27
src/api/plugins/plugins.auth.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { SessionProvider } from '#root/services/sessions/sessions.provider.ts';
|
||||||
|
import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
|
||||||
|
|
||||||
|
const authPlugin: FastifyPluginAsyncZod = async (fastify) => {
|
||||||
|
fastify.addHook('onRequest', async (req, reply) => {
|
||||||
|
const authProvider = req.headers['x-auth-provider'];
|
||||||
|
if (!authProvider || Array.isArray(authProvider)) {
|
||||||
|
throw reply.unauthorized('missing x-auth-provider header');
|
||||||
|
}
|
||||||
|
const authorization = req.headers.authorization;
|
||||||
|
if (!authorization) {
|
||||||
|
throw reply.unauthorized('missing authorization header');
|
||||||
|
}
|
||||||
|
const [type, token] = authorization.split(' ');
|
||||||
|
if (type.toLowerCase() !== 'bearer') {
|
||||||
|
throw reply.unauthorized('only bearer tokens are allowed');
|
||||||
|
}
|
||||||
|
if (!token) {
|
||||||
|
throw reply.unauthorized('missing token');
|
||||||
|
}
|
||||||
|
const sessionProvider = fastify.services.get(SessionProvider);
|
||||||
|
const session = await sessionProvider.get(authProvider, token);
|
||||||
|
req.session = session;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { authPlugin };
|
||||||
@@ -1,15 +1,7 @@
|
|||||||
import type { Services } from '#root/utils/services.ts';
|
import type { Services } from '#root/utils/services.ts';
|
||||||
import { Config } from '#root/config/config.ts';
|
import { Config } from '#root/config/config.ts';
|
||||||
import type { Statement } from './auth.schemas.ts';
|
|
||||||
import type { AuthProvider } from './auth.provider.ts';
|
import type { AuthProvider } from './auth.provider.ts';
|
||||||
|
import { ADMIN_STATEMENTS } from './auth.consts.ts';
|
||||||
const adminStatements: Statement[] = [
|
|
||||||
{
|
|
||||||
effect: 'allow',
|
|
||||||
resources: ['**'],
|
|
||||||
actions: ['**'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
class AdminAuth implements AuthProvider {
|
class AdminAuth implements AuthProvider {
|
||||||
#services: Services;
|
#services: Services;
|
||||||
@@ -24,7 +16,7 @@ class AdminAuth implements AuthProvider {
|
|||||||
throw new Error('Invalid admin token');
|
throw new Error('Invalid admin token');
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
statements: adminStatements,
|
statements: ADMIN_STATEMENTS,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/auth/auth.consts.ts
Normal file
25
src/auth/auth.consts.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Statement } from './auth.schemas.ts';
|
||||||
|
|
||||||
|
const ADMIN_STATEMENTS: Statement[] = [
|
||||||
|
{
|
||||||
|
effect: 'allow',
|
||||||
|
resources: ['**'],
|
||||||
|
actions: ['**'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const WRITER_STATEMENTS: Statement[] = [
|
||||||
|
{
|
||||||
|
effect: 'allow',
|
||||||
|
resources: ['**'],
|
||||||
|
actions: ['mqtt:**'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const READER_STATEMENTS: Statement[] = [
|
||||||
|
{
|
||||||
|
effect: 'allow',
|
||||||
|
resources: ['**'],
|
||||||
|
actions: ['mqtt:read', 'mqtt:subscribe'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export { ADMIN_STATEMENTS, WRITER_STATEMENTS, READER_STATEMENTS };
|
||||||
@@ -8,6 +8,7 @@ import type { Services } from '#root/utils/services.ts';
|
|||||||
import { Config } from '#root/config/config.ts';
|
import { Config } from '#root/config/config.ts';
|
||||||
|
|
||||||
const tokenBodySchema = z.object({
|
const tokenBodySchema = z.object({
|
||||||
|
exp: z.number().optional(),
|
||||||
statements: z.array(statementSchema),
|
statements: z.array(statementSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -32,11 +33,11 @@ class JwtAuth implements AuthProvider {
|
|||||||
|
|
||||||
public getAccess = async (token: string) => {
|
public getAccess = async (token: string) => {
|
||||||
const config = this.#services.get(Config);
|
const config = this.#services.get(Config);
|
||||||
const { jwtSecret: tokenSecret } = config;
|
const { jwtSecret } = config;
|
||||||
if (!tokenSecret) {
|
if (!jwtSecret) {
|
||||||
throw new Error('Token secret does not exist');
|
throw new Error('Token secret does not exist');
|
||||||
}
|
}
|
||||||
const data = jwt.verify(token, tokenSecret);
|
const data = jwt.verify(token, jwtSecret);
|
||||||
const parsed = tokenBodySchema.parse(data);
|
const parsed = tokenBodySchema.parse(data);
|
||||||
return parsed;
|
return parsed;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,28 +5,7 @@ import type { AuthProvider } from './auth.provider.ts';
|
|||||||
|
|
||||||
import type { Services } from '#root/utils/services.ts';
|
import type { Services } from '#root/utils/services.ts';
|
||||||
import { Config } from '#root/config/config.ts';
|
import { Config } from '#root/config/config.ts';
|
||||||
|
import { ADMIN_STATEMENTS, READER_STATEMENTS, WRITER_STATEMENTS } from './auth.consts.ts';
|
||||||
const adminStatements: Statement[] = [
|
|
||||||
{
|
|
||||||
effect: 'allow',
|
|
||||||
resources: ['**'],
|
|
||||||
actions: ['**'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const writerStatements: Statement[] = [
|
|
||||||
{
|
|
||||||
effect: 'allow',
|
|
||||||
resources: ['**'],
|
|
||||||
actions: ['mqtt:**'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const readerStatements: Statement[] = [
|
|
||||||
{
|
|
||||||
effect: 'allow',
|
|
||||||
resources: ['**'],
|
|
||||||
actions: ['mqtt:read', 'mqtt:subscribe'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
class OidcAuth implements AuthProvider {
|
class OidcAuth implements AuthProvider {
|
||||||
#services: Services;
|
#services: Services;
|
||||||
@@ -49,13 +28,13 @@ class OidcAuth implements AuthProvider {
|
|||||||
const groups = data[config.oidc.groupField];
|
const groups = data[config.oidc.groupField];
|
||||||
if (Array.isArray(groups)) {
|
if (Array.isArray(groups)) {
|
||||||
if (config.oidc.groups.admin && groups.includes(config.oidc.groups.admin)) {
|
if (config.oidc.groups.admin && groups.includes(config.oidc.groups.admin)) {
|
||||||
statements = adminStatements;
|
statements = ADMIN_STATEMENTS;
|
||||||
}
|
}
|
||||||
if (config.oidc.groups.writer && groups.includes(config.oidc.groups.writer)) {
|
if (config.oidc.groups.writer && groups.includes(config.oidc.groups.writer)) {
|
||||||
statements = writerStatements;
|
statements = WRITER_STATEMENTS;
|
||||||
}
|
}
|
||||||
if (config.oidc.groups.reader && groups.includes(config.oidc.groups.reader)) {
|
if (config.oidc.groups.reader && groups.includes(config.oidc.groups.reader)) {
|
||||||
statements = readerStatements;
|
statements = READER_STATEMENTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ class Backbone {
|
|||||||
await this.k8s.setup();
|
await this.k8s.setup();
|
||||||
this.sessionProvider.register('k8s', this.#services.get(K8sAuth));
|
this.sessionProvider.register('k8s', this.#services.get(K8sAuth));
|
||||||
}
|
}
|
||||||
if (this.config.http.enabled) {
|
if (this.config.ws.enabled || this.config.api.enabled) {
|
||||||
console.log('starting http');
|
|
||||||
const http = await this.server.getHttpServer();
|
const http = await this.server.getHttpServer();
|
||||||
http.listen({ port: this.config.http.port, host: '0.0.0.0' });
|
http.listen({ port: this.config.http.port, host: '0.0.0.0' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class Config {
|
class Config {
|
||||||
public get jwtSecret() {
|
public get jwtSecret() {
|
||||||
return process.env.TOKEN_SECRET;
|
return process.env.JWT_SECRET;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get adminToken() {
|
public get adminToken() {
|
||||||
@@ -38,14 +38,26 @@ class Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get http() {
|
public get http() {
|
||||||
const enabled = (process.env.HTTP_ENABLED = 'true');
|
|
||||||
const port = process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT) : 8883;
|
const port = process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT) : 8883;
|
||||||
return {
|
return {
|
||||||
enabled,
|
|
||||||
port,
|
port,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get api() {
|
||||||
|
const enabled = process.env.API_ENABLED === 'true';
|
||||||
|
return {
|
||||||
|
enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public get ws() {
|
||||||
|
const enabled = process.env.WS_ENABLED === 'true';
|
||||||
|
return {
|
||||||
|
enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public get tcp() {
|
public get tcp() {
|
||||||
const enabled = (process.env.TCP_ENABLED = 'true');
|
const enabled = (process.env.TCP_ENABLED = 'true');
|
||||||
const port = process.env.TCP_PORT ? parseInt(process.env.TCP_PORT) : 1883;
|
const port = process.env.TCP_PORT ? parseInt(process.env.TCP_PORT) : 1883;
|
||||||
|
|||||||
9
src/dev.ts
Normal file
9
src/dev.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Backbone } from './backbone.ts';
|
||||||
|
process.env.JWT_SECRET = 'test';
|
||||||
|
process.env.ADMIN_TOKEN = 'admin';
|
||||||
|
process.env.API_ENABLED = 'true';
|
||||||
|
|
||||||
|
const backbone = new Backbone();
|
||||||
|
await backbone.start();
|
||||||
|
|
||||||
|
console.log('started');
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
import tcp from 'node:net';
|
import tcp from 'node:net';
|
||||||
import type { IncomingMessage } from 'node:http';
|
import type { IncomingMessage } from 'node:http';
|
||||||
|
|
||||||
|
import swagger from '@fastify/swagger';
|
||||||
|
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
|
||||||
|
import {
|
||||||
|
jsonSchemaTransform,
|
||||||
|
createJsonSchemaTransform,
|
||||||
|
serializerCompiler,
|
||||||
|
validatorCompiler,
|
||||||
|
} from 'fastify-type-provider-zod';
|
||||||
|
import scalar from '@scalar/fastify-api-reference';
|
||||||
import {
|
import {
|
||||||
type AuthenticateHandler,
|
type AuthenticateHandler,
|
||||||
type AuthorizeForwardHandler,
|
type AuthorizeForwardHandler,
|
||||||
@@ -19,6 +28,8 @@ import { TopicsHandler } from '#root/topics/topics.handler.ts';
|
|||||||
import type { Services } from '#root/utils/services.ts';
|
import type { Services } from '#root/utils/services.ts';
|
||||||
import { Session } from '#root/services/sessions/sessions.session.ts';
|
import { Session } from '#root/services/sessions/sessions.session.ts';
|
||||||
import { SessionProvider } from '#root/services/sessions/sessions.provider.ts';
|
import { SessionProvider } from '#root/services/sessions/sessions.provider.ts';
|
||||||
|
import fastifySensible from '@fastify/sensible';
|
||||||
|
import { Config } from '#root/config/config.ts';
|
||||||
|
|
||||||
type Aedes = ReturnType<typeof aedes.createBroker>;
|
type Aedes = ReturnType<typeof aedes.createBroker>;
|
||||||
|
|
||||||
@@ -52,6 +63,10 @@ class MqttServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get bus() {
|
||||||
|
return this.#server;
|
||||||
|
}
|
||||||
|
|
||||||
#authenticate: AuthenticateHandler = async (client, username, password, callback) => {
|
#authenticate: AuthenticateHandler = async (client, username, password, callback) => {
|
||||||
try {
|
try {
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
@@ -112,14 +127,51 @@ class MqttServer {
|
|||||||
|
|
||||||
#setupHttpServer = async () => {
|
#setupHttpServer = async () => {
|
||||||
const http = fastify({});
|
const http = fastify({});
|
||||||
|
const config = this.#services.get(Config);
|
||||||
|
if (config.api.enabled) {
|
||||||
|
http.decorate('services', this.#services);
|
||||||
|
http.setValidatorCompiler(validatorCompiler);
|
||||||
|
http.setSerializerCompiler(serializerCompiler);
|
||||||
await http.register(fastifyWebSocket);
|
await http.register(fastifyWebSocket);
|
||||||
http.get('/ws', { websocket: true }, (socket, req) => {
|
await http.register(fastifySensible);
|
||||||
const stream = createWebSocketStream(socket);
|
await http.register(swagger, {
|
||||||
this.#server.handle(stream, req as unknown as IncomingMessage);
|
openapi: {
|
||||||
|
info: {
|
||||||
|
title: 'Backbone',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
securitySchemes: {
|
||||||
|
authProviderHeader: {
|
||||||
|
type: 'apiKey',
|
||||||
|
name: 'X-Auth-Provider',
|
||||||
|
in: 'header',
|
||||||
|
},
|
||||||
|
bearerAuth: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: [{ bearerAuth: [], authProviderHeader: [] }],
|
||||||
|
},
|
||||||
|
transform: jsonSchemaTransform,
|
||||||
|
});
|
||||||
|
await http.register(scalar, {
|
||||||
|
routePrefix: '/docs',
|
||||||
});
|
});
|
||||||
await http.register(api, {
|
await http.register(api, {
|
||||||
prefix: '/api',
|
prefix: '/api',
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (config.ws.enabled) {
|
||||||
|
http.get('/ws', { websocket: true }, (socket, req) => {
|
||||||
|
const stream = createWebSocketStream(socket);
|
||||||
|
this.#server.handle(stream, req as unknown as IncomingMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await http.ready();
|
||||||
|
http.swagger();
|
||||||
return http;
|
return http;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { AuthProvider } from '#root/auth/auth.provider.ts';
|
import type { AuthProvider } from '#root/auth/auth.provider.ts';
|
||||||
|
import { Session } from './sessions.session.ts';
|
||||||
|
|
||||||
class SessionProvider {
|
class SessionProvider {
|
||||||
#handlers: Map<string, AuthProvider>;
|
#handlers: Map<string, AuthProvider>;
|
||||||
@@ -7,6 +8,10 @@ class SessionProvider {
|
|||||||
this.#handlers = new Map();
|
this.#handlers = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get providers() {
|
||||||
|
return Array.from(this.#handlers.keys());
|
||||||
|
}
|
||||||
|
|
||||||
public register = (name: string, provider: AuthProvider) => {
|
public register = (name: string, provider: AuthProvider) => {
|
||||||
this.#handlers.set(name, provider);
|
this.#handlers.set(name, provider);
|
||||||
};
|
};
|
||||||
@@ -18,6 +23,15 @@ class SessionProvider {
|
|||||||
}
|
}
|
||||||
return handler.getAccess(token);
|
return handler.getAccess(token);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public get = async (provider: string, token: string) => {
|
||||||
|
const handler = this.#handlers.get(provider);
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('Provider not available');
|
||||||
|
}
|
||||||
|
const access = await handler.getAccess(token);
|
||||||
|
return new Session(access);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { SessionProvider };
|
export { SessionProvider };
|
||||||
|
|||||||
Reference in New Issue
Block a user