mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
feat: Adds auto openapi doc generation + swagger UI for API (#779)
* Adds support for auto-openapi doc generation * Adds a swagger route for local development * Adds a script to manually generate the openapi doc (to be used later if we publish to our site, etc...) Ref: HDX-1661
This commit is contained in:
parent
7de8916074
commit
293a2affc8
11 changed files with 1241 additions and 37 deletions
5
.changeset/moody-guests-smell.md
Normal file
5
.changeset/moody-guests-smell.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/api": patch
|
||||
---
|
||||
|
||||
Adds openapidoc annotations for spec generation and swagger route for development
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -35,6 +35,9 @@ packages/app/.vercel
|
|||
packages/app/coverage
|
||||
packages/app/out
|
||||
|
||||
# OpenAPI spec
|
||||
packages/public/openapi.json
|
||||
|
||||
# optional npm cache directory
|
||||
**/.npm
|
||||
|
||||
|
|
|
|||
|
|
@ -18,3 +18,4 @@ PORT=${HYPERDX_API_PORT}
|
|||
REDIS_URL=redis://localhost:6379
|
||||
USAGE_STATS_ENABLED=false
|
||||
NODE_OPTIONS="--max-http-header-size=131072"
|
||||
ENABLE_SWAGGER=true
|
||||
|
|
@ -73,12 +73,15 @@
|
|||
"@types/semver": "^7.3.12",
|
||||
"@types/sqlstring": "^2.3.0",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@types/swagger-jsdoc": "^6",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"jest": "^28.1.3",
|
||||
"migrate-mongo": "^11.0.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"rimraf": "^4.4.1",
|
||||
"supertest": "^6.3.1",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"ts-jest": "^28.0.5",
|
||||
"ts-node": "^10.8.1",
|
||||
"tsc-alias": "^1.8.8",
|
||||
|
|
@ -98,6 +101,7 @@
|
|||
"dev:migrate-db-create": "ts-node node_modules/.bin/migrate-mongo create -f migrate-mongo-config.ts",
|
||||
"dev:migrate-db": "ts-node node_modules/.bin/migrate-mongo up -f migrate-mongo-config.ts",
|
||||
"dev:migrate-ch-create": "migrate create -ext sql -dir ./migrations/ch -seq",
|
||||
"dev:migrate-ch": "migrate -database 'clickhouse://localhost:9000?database=default&x-multi-statement=true' -path ./migrations/ch up"
|
||||
"dev:migrate-ch": "migrate -database 'clickhouse://localhost:9000?database=default&x-multi-statement=true' -path ./migrations/ch up",
|
||||
"docgen": "ts-node scripts/generate-api-docs.ts"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
packages/api/scripts/generate-api-docs.ts
Normal file
12
packages/api/scripts/generate-api-docs.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import swaggerJsdoc from 'swagger-jsdoc';
|
||||
|
||||
import { swaggerOptions } from '../src/utils/swagger';
|
||||
|
||||
const specs = swaggerJsdoc(swaggerOptions);
|
||||
const outputPath = path.resolve(__dirname, '../../public/openapi.json');
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, JSON.stringify(specs, null, 2));
|
||||
|
||||
console.log(`OpenAPI specification written to ${outputPath}`);
|
||||
|
|
@ -18,6 +18,7 @@ import externalRoutersV2 from './routers/external-api/v2';
|
|||
import usageStats from './tasks/usageStats';
|
||||
import { expressLogger } from './utils/logger';
|
||||
import passport from './utils/passport';
|
||||
import { setupSwagger } from './utils/swagger';
|
||||
|
||||
const app: express.Application = express();
|
||||
|
||||
|
|
@ -102,7 +103,15 @@ app.use('/clickhouse-proxy', isUserAuthenticated, clickhouseProxyRouter);
|
|||
// ---------------------------------------------------------------------
|
||||
// ----------------------- External Routers ----------------------------
|
||||
// ---------------------------------------------------------------------
|
||||
// API v1
|
||||
// API v2
|
||||
// Only initialize Swagger in development or if explicitly enabled
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' ||
|
||||
process.env.ENABLE_SWAGGER === 'true'
|
||||
) {
|
||||
setupSwagger(app);
|
||||
}
|
||||
|
||||
app.use('/api/v2', externalRoutersV2);
|
||||
|
||||
// error handling
|
||||
|
|
|
|||
|
|
@ -13,8 +13,241 @@ import {
|
|||
import { translateAlertDocumentToExternalAlert } from '@/utils/externalApi';
|
||||
import { alertSchema, objectIdSchema } from '@/utils/zod';
|
||||
|
||||
const router = express.Router();
|
||||
/**
|
||||
* @openapi
|
||||
* components:
|
||||
* schemas:
|
||||
* Error:
|
||||
* type: object
|
||||
* properties:
|
||||
* message:
|
||||
* type: string
|
||||
* Alert:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a123456"
|
||||
* name:
|
||||
* type: string
|
||||
* example: "High Error Rate"
|
||||
* message:
|
||||
* type: string
|
||||
* example: "Error rate exceeds threshold"
|
||||
* threshold:
|
||||
* type: number
|
||||
* example: 100
|
||||
* interval:
|
||||
* type: string
|
||||
* example: "15m"
|
||||
* thresholdType:
|
||||
* type: string
|
||||
* enum: [above, below]
|
||||
* example: "above"
|
||||
* source:
|
||||
* type: string
|
||||
* enum: [tile, search]
|
||||
* example: "tile"
|
||||
* state:
|
||||
* type: string
|
||||
* example: "inactive"
|
||||
* channel:
|
||||
* type: object
|
||||
* properties:
|
||||
* type:
|
||||
* type: string
|
||||
* example: "webhook"
|
||||
* webhookId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a789012"
|
||||
* team:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a345678"
|
||||
* tileId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a901234"
|
||||
* dashboard:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* savedSearch:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* groupBy:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* silenced:
|
||||
* type: boolean
|
||||
* nullable: true
|
||||
* createdAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* example: "2023-01-01T00:00:00.000Z"
|
||||
* updatedAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* example: "2023-01-01T00:00:00.000Z"
|
||||
*
|
||||
* CreateAlertRequest:
|
||||
* type: object
|
||||
* required:
|
||||
* - threshold
|
||||
* - interval
|
||||
* - source
|
||||
* - thresholdType
|
||||
* - channel
|
||||
* properties:
|
||||
* dashboardId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* tileId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a901234"
|
||||
* threshold:
|
||||
* type: number
|
||||
* example: 100
|
||||
* interval:
|
||||
* type: string
|
||||
* example: "1h"
|
||||
* source:
|
||||
* type: string
|
||||
* enum: [tile, search]
|
||||
* example: "tile"
|
||||
* thresholdType:
|
||||
* type: string
|
||||
* enum: [above, below]
|
||||
* example: "above"
|
||||
* channel:
|
||||
* type: object
|
||||
* properties:
|
||||
* type:
|
||||
* type: string
|
||||
* example: "webhook"
|
||||
* webhookId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a789012"
|
||||
* name:
|
||||
* type: string
|
||||
* example: "Test Alert"
|
||||
* message:
|
||||
* type: string
|
||||
* example: "Test Alert Message"
|
||||
*
|
||||
* UpdateAlertRequest:
|
||||
* type: object
|
||||
* properties:
|
||||
* threshold:
|
||||
* type: number
|
||||
* example: 500
|
||||
* interval:
|
||||
* type: string
|
||||
* example: "1h"
|
||||
* thresholdType:
|
||||
* type: string
|
||||
* enum: [above, below]
|
||||
* example: "above"
|
||||
* source:
|
||||
* type: string
|
||||
* enum: [tile, search]
|
||||
* example: "tile"
|
||||
* dashboardId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* tileId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a901234"
|
||||
* channel:
|
||||
* type: object
|
||||
* properties:
|
||||
* type:
|
||||
* type: string
|
||||
* example: "webhook"
|
||||
* webhookId:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a789012"
|
||||
* name:
|
||||
* type: string
|
||||
* example: "Updated Alert Name"
|
||||
* message:
|
||||
* type: string
|
||||
* example: "Updated message"
|
||||
*
|
||||
* AlertResponse:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* $ref: '#/components/schemas/Alert'
|
||||
*
|
||||
* AlertsListResponse:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Alert'
|
||||
*
|
||||
* EmptyResponse:
|
||||
* type: object
|
||||
* properties: {}
|
||||
*/
|
||||
|
||||
const router = express.Router();
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/alerts/{id}:
|
||||
* get:
|
||||
* summary: Get Alert
|
||||
* description: Retrieves a specific alert by ID
|
||||
* operationId: getAlert
|
||||
* tags: [Alerts]
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Alert ID
|
||||
* example: "65f5e4a3b9e77c001a123456"
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully retrieved alert
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/AlertResponse'
|
||||
* examples:
|
||||
* alertResponse:
|
||||
* summary: Single alert response
|
||||
* value:
|
||||
* data:
|
||||
* id: "65f5e4a3b9e77c001a123456"
|
||||
* name: "CPU Usage Alert"
|
||||
* message: "CPU usage is above 80%"
|
||||
* threshold: 80
|
||||
* interval: "5m"
|
||||
* thresholdType: "above"
|
||||
* source: "tile"
|
||||
* state: "active"
|
||||
* channel:
|
||||
* type: "webhook"
|
||||
* webhookId: "65f5e4a3b9e77c001a789012"
|
||||
* team: "65f5e4a3b9e77c001a345678"
|
||||
* tileId: "65f5e4a3b9e77c001a901234"
|
||||
* dashboard: "65f5e4a3b9e77c001a567890"
|
||||
* createdAt: "2023-03-15T10:20:30.000Z"
|
||||
* updatedAt: "2023-03-15T14:25:10.000Z"
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* '404':
|
||||
* description: Alert not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
validateRequest({
|
||||
|
|
@ -44,6 +277,51 @@ router.get(
|
|||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/alerts:
|
||||
* get:
|
||||
* summary: List Alerts
|
||||
* description: Retrieves a list of all alerts for the authenticated team
|
||||
* operationId: listAlerts
|
||||
* tags: [Alerts]
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully retrieved alerts
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/AlertsListResponse'
|
||||
* examples:
|
||||
* alertsList:
|
||||
* summary: List of alerts
|
||||
* value:
|
||||
* data:
|
||||
* - id: "65f5e4a3b9e77c001a123456"
|
||||
* name: "High Error Rate"
|
||||
* message: "Error rate exceeds threshold"
|
||||
* threshold: 100
|
||||
* interval: "15m"
|
||||
* thresholdType: "above"
|
||||
* source: "tile"
|
||||
* state: "inactive"
|
||||
* channel:
|
||||
* type: "webhook"
|
||||
* webhookId: "65f5e4a3b9e77c001a789012"
|
||||
* team: "65f5e4a3b9e77c001a345678"
|
||||
* tileId: "65f5e4a3b9e77c001a901234"
|
||||
* dashboard: "65f5e4a3b9e77c001a567890"
|
||||
* createdAt: "2023-01-01T00:00:00.000Z"
|
||||
* updatedAt: "2023-01-01T00:00:00.000Z"
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Unauthorized access. API key is missing or invalid."
|
||||
*/
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const teamId = req.user?.team;
|
||||
|
|
@ -61,6 +339,55 @@ router.get('/', async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/alerts:
|
||||
* post:
|
||||
* summary: Create Alert
|
||||
* description: Creates a new alert
|
||||
* operationId: createAlert
|
||||
* tags: [Alerts]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/CreateAlertRequest'
|
||||
* examples:
|
||||
* tileAlert:
|
||||
* summary: Create a tile-based alert
|
||||
* value:
|
||||
* dashboardId: "65f5e4a3b9e77c001a567890"
|
||||
* tileId: "65f5e4a3b9e77c001a901234"
|
||||
* threshold: 100
|
||||
* interval: "1h"
|
||||
* source: "tile"
|
||||
* thresholdType: "above"
|
||||
* channel:
|
||||
* type: "webhook"
|
||||
* webhookId: "65f5e4a3b9e77c001a789012"
|
||||
* name: "Error Spike Alert"
|
||||
* message: "Error rate has exceeded 100 in the last hour"
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully created alert
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/AlertResponse'
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* '500':
|
||||
* description: Server error or validation failure
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
router.post('/', async (req, res, next) => {
|
||||
const teamId = req.user?.team;
|
||||
if (teamId == null) {
|
||||
|
|
@ -78,6 +405,69 @@ router.post('/', async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/alerts/{id}:
|
||||
* put:
|
||||
* summary: Update Alert
|
||||
* description: Updates an existing alert
|
||||
* operationId: updateAlert
|
||||
* tags: [Alerts]
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Alert ID
|
||||
* example: "65f5e4a3b9e77c001a123456"
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/UpdateAlertRequest'
|
||||
* examples:
|
||||
* updateAlert:
|
||||
* summary: Update alert properties
|
||||
* value:
|
||||
* threshold: 500
|
||||
* interval: "1h"
|
||||
* thresholdType: "above"
|
||||
* source: "tile"
|
||||
* dashboardId: "65f5e4a3b9e77c001a567890"
|
||||
* tileId: "65f5e4a3b9e77c001a901234"
|
||||
* channel:
|
||||
* type: "webhook"
|
||||
* webhookId: "65f5e4a3b9e77c001a789012"
|
||||
* name: "Updated Alert Name"
|
||||
* message: "Updated threshold and interval"
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully updated alert
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/AlertResponse'
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* '404':
|
||||
* description: Alert not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* '500':
|
||||
* description: Server error or validation failure
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
validateRequest({
|
||||
|
|
@ -111,6 +501,43 @@ router.put(
|
|||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/alerts/{id}:
|
||||
* delete:
|
||||
* summary: Delete Alert
|
||||
* description: Deletes an alert
|
||||
* operationId: deleteAlert
|
||||
* tags: [Alerts]
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Alert ID
|
||||
* example: "65f5e4a3b9e77c001a123456"
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully deleted alert
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/EmptyResponse'
|
||||
* example: {}
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* '404':
|
||||
* description: Alert not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
validateRequest({
|
||||
|
|
|
|||
|
|
@ -22,8 +22,249 @@ import {
|
|||
tagsSchema,
|
||||
} from '@/utils/zod';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* components:
|
||||
* schemas:
|
||||
* ChartSeries:
|
||||
* type: object
|
||||
* properties:
|
||||
* type:
|
||||
* type: string
|
||||
* enum: [time, table, number, histogram, search, markdown]
|
||||
* example: "time"
|
||||
* dataSource:
|
||||
* type: string
|
||||
* enum: [events, metrics]
|
||||
* example: "events"
|
||||
* aggFn:
|
||||
* type: string
|
||||
* example: "count"
|
||||
* where:
|
||||
* type: string
|
||||
* example: "level:error"
|
||||
* groupBy:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: []
|
||||
*
|
||||
* Tile:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a901234"
|
||||
* name:
|
||||
* type: string
|
||||
* example: "Error Rate"
|
||||
* x:
|
||||
* type: integer
|
||||
* example: 0
|
||||
* y:
|
||||
* type: integer
|
||||
* example: 0
|
||||
* w:
|
||||
* type: integer
|
||||
* example: 6
|
||||
* h:
|
||||
* type: integer
|
||||
* example: 3
|
||||
* asRatio:
|
||||
* type: boolean
|
||||
* example: false
|
||||
* series:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/ChartSeries'
|
||||
*
|
||||
* Dashboard:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* name:
|
||||
* type: string
|
||||
* example: "Service Overview"
|
||||
* tiles:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Tile'
|
||||
* tags:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: ["production", "monitoring"]
|
||||
*
|
||||
* CreateDashboardRequest:
|
||||
* type: object
|
||||
* required:
|
||||
* - name
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* example: "New Dashboard"
|
||||
* tiles:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Tile'
|
||||
* tags:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: ["development"]
|
||||
*
|
||||
* UpdateDashboardRequest:
|
||||
* type: object
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* example: "Updated Dashboard Name"
|
||||
* tiles:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Tile'
|
||||
* tags:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* example: ["production", "updated"]
|
||||
*
|
||||
* DashboardResponse:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* $ref: '#/components/schemas/Dashboard'
|
||||
*
|
||||
* DashboardsListResponse:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Dashboard'
|
||||
*/
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/dashboards:
|
||||
* get:
|
||||
* summary: List Dashboards
|
||||
* description: Retrieves a list of all dashboards for the authenticated team
|
||||
* operationId: listDashboards
|
||||
* tags: [Dashboards]
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully retrieved dashboards
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/DashboardsListResponse'
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
*/
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const teamId = req.user?.team;
|
||||
if (teamId == null) {
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
|
||||
const dashboards = await Dashboard.find(
|
||||
{ team: teamId },
|
||||
{ _id: 1, name: 1, tiles: 1, tags: 1 },
|
||||
).sort({ name: -1 });
|
||||
|
||||
res.json({
|
||||
data: dashboards.map(d =>
|
||||
translateDashboardDocumentToExternalDashboard(d),
|
||||
),
|
||||
});
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/dashboards/{id}:
|
||||
* get:
|
||||
* summary: Get Dashboard
|
||||
* description: Retrieves a specific dashboard by ID
|
||||
* operationId: getDashboard
|
||||
* tags: [Dashboards]
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Dashboard ID
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully retrieved dashboard
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/DashboardResponse'
|
||||
* examples:
|
||||
* dashboard:
|
||||
* summary: Single dashboard response
|
||||
* value:
|
||||
* data:
|
||||
* id: "65f5e4a3b9e77c001a567890"
|
||||
* name: "Infrastructure Monitoring"
|
||||
* tiles:
|
||||
* - id: "65f5e4a3b9e77c001a901234"
|
||||
* name: "Server CPU"
|
||||
* x: 0
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "metrics"
|
||||
* aggFn: "avg"
|
||||
* field: "cpu.usage"
|
||||
* where: "host:server-01"
|
||||
* groupBy: []
|
||||
* - id: "65f5e4a3b9e77c001a901235"
|
||||
* name: "Memory Usage"
|
||||
* x: 6
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "metrics"
|
||||
* aggFn: "avg"
|
||||
* field: "memory.usage"
|
||||
* where: "host:server-01"
|
||||
* groupBy: []
|
||||
* tags: ["infrastructure", "monitoring"]
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Unauthorized access. API key is missing or invalid."
|
||||
* '404':
|
||||
* description: Dashboard not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Dashboard not found"
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
validateRequest({
|
||||
|
|
@ -56,28 +297,116 @@ router.get(
|
|||
},
|
||||
);
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const teamId = req.user?.team;
|
||||
if (teamId == null) {
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
|
||||
const dashboards = await Dashboard.find(
|
||||
{ team: teamId },
|
||||
{ _id: 1, name: 1, tiles: 1, tags: 1 },
|
||||
).sort({ name: -1 });
|
||||
|
||||
res.json({
|
||||
data: dashboards.map(d =>
|
||||
translateDashboardDocumentToExternalDashboard(d),
|
||||
),
|
||||
});
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/dashboards:
|
||||
* post:
|
||||
* summary: Create Dashboard
|
||||
* description: Creates a new dashboard
|
||||
* operationId: createDashboard
|
||||
* tags: [Dashboards]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/CreateDashboardRequest'
|
||||
* examples:
|
||||
* simpleTimeSeriesDashboard:
|
||||
* summary: Dashboard with time series chart
|
||||
* value:
|
||||
* name: "API Monitoring Dashboard"
|
||||
* tiles:
|
||||
* - name: "API Request Volume"
|
||||
* x: 0
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "service:api"
|
||||
* groupBy: []
|
||||
* tags: ["api", "monitoring"]
|
||||
* complexDashboard:
|
||||
* summary: Dashboard with multiple chart types
|
||||
* value:
|
||||
* name: "Service Health Overview"
|
||||
* tiles:
|
||||
* - name: "Request Count"
|
||||
* x: 0
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "service:backend"
|
||||
* groupBy: []
|
||||
* - name: "Error Distribution"
|
||||
* x: 6
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "table"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "level:error"
|
||||
* groupBy: ["errorType"]
|
||||
* sortOrder: "desc"
|
||||
* tags: ["service-health", "production"]
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully created dashboard
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/DashboardResponse'
|
||||
* examples:
|
||||
* createdDashboard:
|
||||
* summary: Created dashboard response
|
||||
* value:
|
||||
* data:
|
||||
* id: "65f5e4a3b9e77c001a567890"
|
||||
* name: "API Monitoring Dashboard"
|
||||
* tiles:
|
||||
* - id: "65f5e4a3b9e77c001a901234"
|
||||
* name: "API Request Volume"
|
||||
* x: 0
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "service:api"
|
||||
* groupBy: []
|
||||
* tags: ["api", "monitoring"]
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Unauthorized access. API key is missing or invalid."
|
||||
* '500':
|
||||
* description: Server error or validation failure
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Dashboard validation failed: name is required"
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
validateRequest({
|
||||
|
|
@ -121,6 +450,125 @@ router.post(
|
|||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/dashboards/{id}:
|
||||
* put:
|
||||
* summary: Update Dashboard
|
||||
* description: Updates an existing dashboard
|
||||
* operationId: updateDashboard
|
||||
* tags: [Dashboards]
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Dashboard ID
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/UpdateDashboardRequest'
|
||||
* examples:
|
||||
* updateDashboard:
|
||||
* summary: Update dashboard properties and tiles
|
||||
* value:
|
||||
* name: "Updated Dashboard Name"
|
||||
* tiles:
|
||||
* - id: "65f5e4a3b9e77c001a901234"
|
||||
* name: "Updated Time Series Chart"
|
||||
* x: 0
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "level:error"
|
||||
* groupBy: []
|
||||
* - name: "New Number Chart"
|
||||
* x: 6
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "number"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "level:info"
|
||||
* tags: ["production", "updated"]
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully updated dashboard
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/DashboardResponse'
|
||||
* examples:
|
||||
* updatedDashboard:
|
||||
* summary: Updated dashboard response
|
||||
* value:
|
||||
* data:
|
||||
* id: "65f5e4a3b9e77c001a567890"
|
||||
* name: "Updated Dashboard Name"
|
||||
* tiles:
|
||||
* - id: "65f5e4a3b9e77c001a901234"
|
||||
* name: "Updated Time Series Chart"
|
||||
* x: 0
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "time"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "level:error"
|
||||
* groupBy: []
|
||||
* - id: "65f5e4a3b9e77c001a901236"
|
||||
* name: "New Number Chart"
|
||||
* x: 6
|
||||
* y: 0
|
||||
* w: 6
|
||||
* h: 3
|
||||
* asRatio: false
|
||||
* series:
|
||||
* - type: "number"
|
||||
* dataSource: "events"
|
||||
* aggFn: "count"
|
||||
* where: "level:info"
|
||||
* tags: ["production", "updated"]
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Unauthorized access. API key is missing or invalid."
|
||||
* '404':
|
||||
* description: Dashboard not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Dashboard not found"
|
||||
* '500':
|
||||
* description: Server error or validation failure
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Invalid dashboard configuration"
|
||||
*/
|
||||
router.put(
|
||||
'/:id',
|
||||
validateRequest({
|
||||
|
|
@ -183,6 +631,47 @@ router.put(
|
|||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/dashboards/{id}:
|
||||
* delete:
|
||||
* summary: Delete Dashboard
|
||||
* description: Deletes a dashboard
|
||||
* operationId: deleteDashboard
|
||||
* tags: [Dashboards]
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Dashboard ID
|
||||
* example: "65f5e4a3b9e77c001a567890"
|
||||
* responses:
|
||||
* '200':
|
||||
* description: Successfully deleted dashboard
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/EmptyResponse'
|
||||
* example: {}
|
||||
* '401':
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Unauthorized access. API key is missing or invalid."
|
||||
* '404':
|
||||
* description: Dashboard not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* example:
|
||||
* message: "Dashboard not found"
|
||||
*/
|
||||
router.delete(
|
||||
'/:id',
|
||||
validateRequest({
|
||||
|
|
|
|||
70
packages/api/src/utils/swagger.ts
Normal file
70
packages/api/src/utils/swagger.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { Application, Express } from 'express';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import swaggerJsdoc from 'swagger-jsdoc';
|
||||
import swaggerUi from 'swagger-ui-express';
|
||||
|
||||
export const swaggerOptions = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: 'HyperDX External API',
|
||||
description: 'API for managing HyperDX alerts and dashboards',
|
||||
version: '2.0.0',
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: 'https://api.hyperdx.io',
|
||||
description: 'Production API server',
|
||||
},
|
||||
{
|
||||
url: '/',
|
||||
description: 'Current server',
|
||||
},
|
||||
],
|
||||
tags: [
|
||||
{
|
||||
name: 'Dashboards',
|
||||
description:
|
||||
'Endpoints for managing dashboards and their visualizations',
|
||||
},
|
||||
{
|
||||
name: 'Alerts',
|
||||
description: 'Endpoints for managing monitoring alerts',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
BearerAuth: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'API Key',
|
||||
},
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{
|
||||
BearerAuth: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
apis: ['./src/routers/external-api/**/*.ts'], // Path to the API routes files
|
||||
};
|
||||
|
||||
export function setupSwagger(app: Application) {
|
||||
const specs = swaggerJsdoc(swaggerOptions);
|
||||
|
||||
// Serve swagger docs
|
||||
app.use('/api/v2/docs', swaggerUi.serve, swaggerUi.setup(specs));
|
||||
|
||||
// Serve OpenAPI spec as JSON (needed for ReDoc)
|
||||
app.get('/api/v2/docs.json', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(specs);
|
||||
});
|
||||
|
||||
// Optionally save the spec to a file
|
||||
const outputPath = path.resolve(__dirname, '../../../public/openapi.json');
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, JSON.stringify(specs, null, 2));
|
||||
}
|
||||
|
|
@ -24,6 +24,6 @@
|
|||
"strict": true,
|
||||
"target": "ES2022"
|
||||
},
|
||||
"include": ["src", "migrations"],
|
||||
"include": ["src", "migrations", "scripts"],
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
||||
|
|
|
|||
206
yarn.lock
206
yarn.lock
|
|
@ -29,6 +29,48 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@apidevtools/json-schema-ref-parser@npm:^9.0.6":
|
||||
version: 9.1.2
|
||||
resolution: "@apidevtools/json-schema-ref-parser@npm:9.1.2"
|
||||
dependencies:
|
||||
"@jsdevtools/ono": "npm:^7.1.3"
|
||||
"@types/json-schema": "npm:^7.0.6"
|
||||
call-me-maybe: "npm:^1.0.1"
|
||||
js-yaml: "npm:^4.1.0"
|
||||
checksum: 10c0/ebf952eb2e00bf0919f024e72897e047fd5012f0a9e47ac361873f6de0a733b9334513cdbc73205a6b43ac4a652b8c87f55e489c39b2d60bd0bc1cb2b411e218
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@apidevtools/openapi-schemas@npm:^2.0.4":
|
||||
version: 2.1.0
|
||||
resolution: "@apidevtools/openapi-schemas@npm:2.1.0"
|
||||
checksum: 10c0/f4aa0f9df32e474d166c84ef91bceb18fa1c4f44b5593879529154ef340846811ea57dc2921560f157f692262827d28d988dd6e19fb21f00320e9961964176b4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@apidevtools/swagger-methods@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "@apidevtools/swagger-methods@npm:3.0.2"
|
||||
checksum: 10c0/8c390e8e50c0be7787ba0ba4c3758488bde7c66c2d995209b4b48c1f8bc988faf393cbb24a4bd1cd2d42ce5167c26538e8adea5c85eb922761b927e4dab9fa1c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@apidevtools/swagger-parser@npm:10.0.3":
|
||||
version: 10.0.3
|
||||
resolution: "@apidevtools/swagger-parser@npm:10.0.3"
|
||||
dependencies:
|
||||
"@apidevtools/json-schema-ref-parser": "npm:^9.0.6"
|
||||
"@apidevtools/openapi-schemas": "npm:^2.0.4"
|
||||
"@apidevtools/swagger-methods": "npm:^3.0.2"
|
||||
"@jsdevtools/ono": "npm:^7.1.3"
|
||||
call-me-maybe: "npm:^1.0.1"
|
||||
z-schema: "npm:^5.0.1"
|
||||
peerDependencies:
|
||||
openapi-types: ">=7"
|
||||
checksum: 10c0/3b43f719c2d647ac8dcf30f132834d413ce21cbf7a8d9c3b35ec91149dd25d608c8fd892358fcd61a8edd8c5140a7fb13676f948e2d87067d081a47b8c7107e9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@aw-web-design/x-default-browser@npm:1.4.126":
|
||||
version: 1.4.126
|
||||
resolution: "@aw-web-design/x-default-browser@npm:1.4.126"
|
||||
|
|
@ -4242,6 +4284,7 @@ __metadata:
|
|||
"@types/semver": "npm:^7.3.12"
|
||||
"@types/sqlstring": "npm:^2.3.0"
|
||||
"@types/supertest": "npm:^2.0.12"
|
||||
"@types/swagger-jsdoc": "npm:^6"
|
||||
"@types/uuid": "npm:^8.3.4"
|
||||
axios: "npm:^1.6.2"
|
||||
compression: "npm:^1.7.4"
|
||||
|
|
@ -4281,6 +4324,8 @@ __metadata:
|
|||
serialize-error: "npm:^8.1.0"
|
||||
sqlstring: "npm:^2.3.3"
|
||||
supertest: "npm:^6.3.1"
|
||||
swagger-jsdoc: "npm:^6.2.8"
|
||||
swagger-ui-express: "npm:^5.0.1"
|
||||
ts-jest: "npm:^28.0.5"
|
||||
ts-node: "npm:^10.8.1"
|
||||
tsc-alias: "npm:^1.8.8"
|
||||
|
|
@ -5264,6 +5309,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@jsdevtools/ono@npm:^7.1.3":
|
||||
version: 7.1.3
|
||||
resolution: "@jsdevtools/ono@npm:7.1.3"
|
||||
checksum: 10c0/a9f7e3e8e3bc315a34959934a5e2f874c423cf4eae64377d3fc9de0400ed9f36cb5fd5ebce3300d2e8f4085f557c4a8b591427a583729a87841fda46e6c216b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0":
|
||||
version: 1.2.1
|
||||
resolution: "@lezer/common@npm:1.2.1"
|
||||
|
|
@ -7982,6 +8034,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@scarf/scarf@npm:=1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "@scarf/scarf@npm:1.4.0"
|
||||
checksum: 10c0/332118bb488e7a70eaad068fb1a33f016d30442fb0498b37a80cb425c1e741853a5de1a04dce03526ed6265481ecf744aa6e13f072178d19e6b94b19f623ae1c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/core@npm:^8.7.0":
|
||||
version: 8.13.0
|
||||
resolution: "@sentry/core@npm:8.13.0"
|
||||
|
|
@ -9823,7 +9882,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8":
|
||||
"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.8":
|
||||
version: 7.0.15
|
||||
resolution: "@types/json-schema@npm:7.0.15"
|
||||
checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
|
||||
|
|
@ -10382,6 +10441,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/swagger-jsdoc@npm:^6":
|
||||
version: 6.0.4
|
||||
resolution: "@types/swagger-jsdoc@npm:6.0.4"
|
||||
checksum: 10c0/fbe17d91a12e1e60a255b02e6def6877c81b356c75ffcd0e5167fbaf1476e2d6600cd7eea79e6b3e0ff7929dec33ade345147509ed3b98026f63c782b74514f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/tedious@npm:^4.0.10":
|
||||
version: 4.0.14
|
||||
resolution: "@types/tedious@npm:4.0.14"
|
||||
|
|
@ -12453,6 +12519,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"call-me-maybe@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "call-me-maybe@npm:1.0.2"
|
||||
checksum: 10c0/8eff5dbb61141ebb236ed71b4e9549e488bcb5451c48c11e5667d5c75b0532303788a1101e6978cafa2d0c8c1a727805599c2741e3e0982855c9f1d78cd06c9f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"callsites@npm:^3.0.0":
|
||||
version: 3.1.0
|
||||
resolution: "callsites@npm:3.1.0"
|
||||
|
|
@ -13101,6 +13174,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:6.2.0":
|
||||
version: 6.2.0
|
||||
resolution: "commander@npm:6.2.0"
|
||||
checksum: 10c0/1b701c6726fc2b6c6a7d9ab017be9465153546a05767cdd0e15e9f9a11c07f88f64d47684b90b07e5fb103d173efb6afdf4a21f6d6c4c25f7376bd027d21062c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^2.19.0, commander@npm:^2.20.0":
|
||||
version: 2.20.3
|
||||
resolution: "commander@npm:2.20.3"
|
||||
|
|
@ -14201,6 +14281,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"doctrine@npm:3.0.0, doctrine@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "doctrine@npm:3.0.0"
|
||||
dependencies:
|
||||
esutils: "npm:^2.0.2"
|
||||
checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"doctrine@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "doctrine@npm:2.1.0"
|
||||
|
|
@ -14210,15 +14299,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"doctrine@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "doctrine@npm:3.0.0"
|
||||
dependencies:
|
||||
esutils: "npm:^2.0.2"
|
||||
checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-accessibility-api@npm:^0.5.9":
|
||||
version: 0.5.16
|
||||
resolution: "dom-accessibility-api@npm:0.5.16"
|
||||
|
|
@ -16784,6 +16864,20 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:7.1.6":
|
||||
version: 7.1.6
|
||||
resolution: "glob@npm:7.1.6"
|
||||
dependencies:
|
||||
fs.realpath: "npm:^1.0.0"
|
||||
inflight: "npm:^1.0.4"
|
||||
inherits: "npm:2"
|
||||
minimatch: "npm:^3.0.4"
|
||||
once: "npm:^1.3.0"
|
||||
path-is-absolute: "npm:^1.0.0"
|
||||
checksum: 10c0/2575cce9306ac534388db751f0aa3e78afedb6af8f3b529ac6b2354f66765545145dba8530abf7bff49fb399a047d3f9b6901c38ee4c9503f592960d9af67763
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:7.1.7":
|
||||
version: 7.1.7
|
||||
resolution: "glob@npm:7.1.7"
|
||||
|
|
@ -19678,7 +19772,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.isequal@npm:^4.0.0":
|
||||
"lodash.get@npm:^4.4.2":
|
||||
version: 4.4.2
|
||||
resolution: "lodash.get@npm:4.4.2"
|
||||
checksum: 10c0/48f40d471a1654397ed41685495acb31498d5ed696185ac8973daef424a749ca0c7871bf7b665d5c14f5cc479394479e0307e781f61d5573831769593411be6e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.isequal@npm:^4.0.0, lodash.isequal@npm:^4.5.0":
|
||||
version: 4.5.0
|
||||
resolution: "lodash.isequal@npm:4.5.0"
|
||||
checksum: 10c0/dfdb2356db19631a4b445d5f37868a095e2402292d59539a987f134a8778c62a2810c2452d11ae9e6dcac71fc9de40a6fedcb20e2952a15b431ad8b29e50e28f
|
||||
|
|
@ -19720,6 +19821,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.mergewith@npm:^4.6.2":
|
||||
version: 4.6.2
|
||||
resolution: "lodash.mergewith@npm:4.6.2"
|
||||
checksum: 10c0/4adbed65ff96fd65b0b3861f6899f98304f90fd71e7f1eb36c1270e05d500ee7f5ec44c02ef979b5ddbf75c0a0b9b99c35f0ad58f4011934c4d4e99e5200b3b5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.sortby@npm:^4.7.0":
|
||||
version: 4.7.0
|
||||
resolution: "lodash.sortby@npm:4.7.0"
|
||||
|
|
@ -26638,6 +26746,51 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swagger-jsdoc@npm:^6.2.8":
|
||||
version: 6.2.8
|
||||
resolution: "swagger-jsdoc@npm:6.2.8"
|
||||
dependencies:
|
||||
commander: "npm:6.2.0"
|
||||
doctrine: "npm:3.0.0"
|
||||
glob: "npm:7.1.6"
|
||||
lodash.mergewith: "npm:^4.6.2"
|
||||
swagger-parser: "npm:^10.0.3"
|
||||
yaml: "npm:2.0.0-1"
|
||||
bin:
|
||||
swagger-jsdoc: bin/swagger-jsdoc.js
|
||||
checksum: 10c0/7e20f08e8d90cc1e787cd82c096291cf12533359f89c70fbe4295a01f7c4734f2e82a03ba94027127bcd3da04b817abfe979f00d00ef0cd8283e449250a66215
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swagger-parser@npm:^10.0.3":
|
||||
version: 10.0.3
|
||||
resolution: "swagger-parser@npm:10.0.3"
|
||||
dependencies:
|
||||
"@apidevtools/swagger-parser": "npm:10.0.3"
|
||||
checksum: 10c0/d1a5c05f651f21a23508a36416071630b83e91dfffd52a6d44b06ca2cd1b86304c0dd2f4c04526c999b70062fa89bde3f5d54a1436626f4350590b6c6265a098
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swagger-ui-dist@npm:>=5.0.0":
|
||||
version: 5.21.0
|
||||
resolution: "swagger-ui-dist@npm:5.21.0"
|
||||
dependencies:
|
||||
"@scarf/scarf": "npm:=1.4.0"
|
||||
checksum: 10c0/fed58b709cc956f5965cc3e2c522898365ecd43f5bb942252d7352e52039ef7b2106b915165295114009ce7e1b64c2b2cb97edf93e3f2a44adfed70bb8a4fac8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swagger-ui-express@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "swagger-ui-express@npm:5.0.1"
|
||||
dependencies:
|
||||
swagger-ui-dist: "npm:>=5.0.0"
|
||||
peerDependencies:
|
||||
express: ">=4.0.0 || >=5.0.0-beta"
|
||||
checksum: 10c0/dbe9830caef7fe455241e44e74958bac62642997e4341c1b0f38a3d684d19a4a81b431217c656792d99f046a1b5f261abf7783ede0afe41098cd4450401f6fd1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"symbol-tree@npm:^3.2.4":
|
||||
version: 3.2.4
|
||||
resolution: "symbol-tree@npm:3.2.4"
|
||||
|
|
@ -28223,6 +28376,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"validator@npm:^13.7.0":
|
||||
version: 13.15.0
|
||||
resolution: "validator@npm:13.15.0"
|
||||
checksum: 10c0/0f13fd7031ac575e8d7828431da8ef5859bac6a38ee65e1d7fdd367dbf1c3d94d95182aecc3183f7fa7a30ff4474bf864d1aff54707620227a2cdbfd36d894c2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vary@npm:^1, vary@npm:~1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "vary@npm:1.1.2"
|
||||
|
|
@ -28879,6 +29039,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yaml@npm:2.0.0-1":
|
||||
version: 2.0.0-1
|
||||
resolution: "yaml@npm:2.0.0-1"
|
||||
checksum: 10c0/e76eba2fbae37cd3e5bff057184be7cdca849895149d2f5660386871a501d76d2e1ec5906c48269a9fe798f214df31d342675b37bcd9d09af7c12eb6fb46a740
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yaml@npm:^1.10.0":
|
||||
version: 1.10.2
|
||||
resolution: "yaml@npm:1.10.2"
|
||||
|
|
@ -28980,6 +29147,23 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"z-schema@npm:^5.0.1":
|
||||
version: 5.0.5
|
||||
resolution: "z-schema@npm:5.0.5"
|
||||
dependencies:
|
||||
commander: "npm:^9.4.1"
|
||||
lodash.get: "npm:^4.4.2"
|
||||
lodash.isequal: "npm:^4.5.0"
|
||||
validator: "npm:^13.7.0"
|
||||
dependenciesMeta:
|
||||
commander:
|
||||
optional: true
|
||||
bin:
|
||||
z-schema: bin/z-schema
|
||||
checksum: 10c0/e4c812cfe6468c19b2a21d07d4ff8fb70359062d33400b45f89017eaa3efe9d51e85963f2b115eaaa99a16b451782249bf9b1fa8b31d35cc473e7becb3e44264
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod-express-middleware@npm:^1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "zod-express-middleware@npm:1.4.0"
|
||||
|
|
|
|||
Loading…
Reference in a new issue