@bull-board/api
Version:
A Dashboard server API built on top of bull or bullmq.
402 lines (336 loc) • 10.6 kB
TypeScript
import type { RedisInfo } from 'redis-info';
import type { BaseAdapter } from '../baseAdapter';
import type { STATUSES } from '../dist/constants/statuses';
export type JobCleanStatus = 'completed' | 'wait' | 'active' | 'delayed' | 'failed';
export type JobRetryStatus = 'completed' | 'failed';
export type MetricsType = 'completed' | 'failed';
export interface QueueMetrics {
meta: {
count: number;
prevTS: number;
prevCount: number;
};
data: number[];
count: number;
}
export type MetricsHistoryGranularity = 'hour' | 'day';
export interface MetricsHistoryQuery {
/** Queue name (namespaced, as returned by adapter.getName()). Omit for the cross-queue global rollup. */
queue?: string;
metric: MetricsType;
/** Inclusive lower bound, epoch ms. */
from: number;
/** Inclusive upper bound, epoch ms. */
to: number;
granularity: MetricsHistoryGranularity;
}
export interface MetricsHistoryPoint {
/** Bucket start, epoch ms (UTC-aligned to the granularity). */
ts: number;
value: number;
}
export interface MetricsHistoryTierUsage {
keys: number;
bytes: number;
}
export interface MetricsHistoryQueueUsage {
queue: string;
keys: number;
bytes: number;
minutes: number;
days: string[];
tiers: Record<'minute' | 'hour' | 'day', MetricsHistoryTierUsage>;
}
export interface MetricsHistoryUsage {
keys: number;
bytes: number;
minutes: number;
oldestDay: string | null;
newestDay: string | null;
tiers: Record<'minute' | 'hour' | 'day', MetricsHistoryTierUsage>;
queues: MetricsHistoryQueueUsage[];
}
export interface MetricsHistoryPurgeOptions {
queue?: string;
/** ISO `YYYY-MM-DD`. Drops days strictly before it; omit to drop everything in scope. */
before?: string;
}
export interface MetricsHistoryPurgeResult {
keysDeleted: number;
fieldsDeleted: number;
}
/**
* Seam the core uses to serve long-retention metrics history.
* The concrete implementation lives in the opt-in @bull-board/metrics package.
* The core never stores anything; it only calls this interface.
*
* `getUsage` and `purge` are optional. Their routes are registered only when a provider
* implements them, so a custom read-only provider stays valid and the UI never offers a
* storage panel that has nothing behind it.
*/
export interface MetricsHistoryProvider {
getHistory(query: MetricsHistoryQuery): Promise<MetricsHistoryPoint[]>;
getUsage?(): Promise<MetricsHistoryUsage>;
purge?(options: MetricsHistoryPurgeOptions): Promise<MetricsHistoryPurgeResult>;
}
type Library = 'bull' | 'bullmq';
type BullMQStatuses = STATUSES;
type BullStatuses = Exclude<BullMQStatuses, 'prioritized' | 'waiting-children'>;
export type Status<Lib extends Library = 'bullmq'> = Lib extends 'bullmq'
? BullMQStatuses
: Lib extends 'bull'
? BullStatuses
: never;
export type JobStatus<Lib extends Library = 'bullmq'> = Lib extends 'bullmq'
? Exclude<BullMQStatuses, 'latest'>
: Lib extends 'bull'
? Exclude<BullStatuses, 'latest'>
: never;
export type JobCounts = Record<Status, number>;
export type ExternalJobUrl = {
displayText?: string;
href: string;
};
export interface QueueAdapterOptions {
readOnlyMode: boolean;
allowRetries: boolean;
prefix: string;
description: string;
displayName: string;
delimiter: string;
externalJobUrl?: (job: QueueJobJson) => ExternalJobUrl;
jobDataSchema?: Record<string, any>;
}
export type BullBoardQueues = Map<string, BaseAdapter>;
export interface QueueJob {
repeatJobKey?: string;
opts: {
delay?: number | undefined;
};
promote(): Promise<void>;
remove(): Promise<void>;
retry(state?: JobRetryStatus): Promise<void>;
toJSON(): QueueJobJson;
getState(): Promise<Status | 'stuck' | 'waiting-children' | 'prioritized' | 'unknown'>;
update?(jobData: Record<string, any>): Promise<void>;
updateData?(jobData: Record<string, any>): Promise<void>;
}
export interface QueueJobJson {
// add properties as needed from real Bull/BullMQ jobs
id?: string | undefined | number | null;
name: string;
progress: string | boolean | number | object;
attemptsMade: number;
finishedOn?: number | null;
processedOn?: number | null;
processedBy?: string | null;
delay?: number;
timestamp: number;
failedReason: string;
stacktrace: string[] | null;
data: any;
returnvalue: any;
opts: any;
parentKey?: string;
repeatJobKey?: string;
}
export interface QueueJobOptions {
delay?: number;
attempts?: number;
}
export type JobRetentionOption = boolean | number | { age?: number; count?: number };
export interface QueueDefaultJobOptions {
attempts?: number;
delay?: number;
priority?: number;
lifo?: boolean;
backoff?: number | { type: string; delay?: number };
removeOnComplete?: JobRetentionOption;
removeOnFail?: JobRetentionOption;
[option: string]: unknown;
}
export interface RedisStats {
version: string;
mode: RedisInfo['redis_mode'];
port: number;
os: string;
uptime: number;
memory: {
total: number;
used: number;
fragmentationRatio: number;
peak: number;
};
clients: {
connected: number;
blocked: number;
};
}
export interface AppJob {
id: QueueJobJson['id'];
name: QueueJobJson['name'];
timestamp: QueueJobJson['timestamp'];
processedOn?: QueueJobJson['processedOn'];
processedBy?: QueueJobJson['processedBy'];
finishedOn?: QueueJobJson['finishedOn'];
progress: QueueJobJson['progress'];
attempts: QueueJobJson['attemptsMade'];
failedReason: QueueJobJson['failedReason'];
stacktrace: string[];
delay: number | undefined;
opts: QueueJobJson['opts'];
data: QueueJobJson['data'];
returnValue: QueueJobJson['returnvalue'];
isFailed: boolean;
externalUrl?: {
displayText?: string;
href: string;
};
groupId?: string | number;
}
export interface JobFlow {
nodeId: string;
isFlowNode: boolean;
flowRoot: FlowNode | null;
}
export interface FlowNode {
id: string;
name: string;
state: string;
progress: string | boolean | number | object;
queueName: string;
children: FlowNode[];
}
export type QueueType = 'bull' | 'bullmq';
export interface AppQueue {
delimiter: string;
name: string;
displayName?: string;
description?: string;
counts: Record<Status, number>;
jobs: AppJob[];
statuses: Status[];
pagination: Pagination;
readOnlyMode: boolean;
allowRetries: boolean;
allowCompletedRetries: boolean;
isPaused: boolean;
type: QueueType;
globalConcurrency: number | null;
}
export type HTTPMethod = 'get' | 'post' | 'put' | 'patch';
export type HTTPStatus = 200 | 204 | 400 | 403 | 404 | 405 | 500;
export interface BullBoardRequest {
queues: BullBoardQueues;
uiConfig: UIConfig;
query: Record<string, any>;
params: Record<string, any>;
body: Record<string, any>;
headers: Record<string, string | undefined>;
}
export type ControllerHandlerReturnType = {
status?: HTTPStatus;
body: string | Record<string, any>;
};
export type ViewHandlerReturnType = {
name: string;
params: Record<string, string>;
};
export type Promisify<T> = T | Promise<T>;
export interface AppControllerRoute {
method: HTTPMethod | HTTPMethod[];
route: string | string[];
handler(request?: BullBoardRequest): Promisify<ControllerHandlerReturnType>;
}
export interface AppViewRoute {
method: HTTPMethod;
route: string | string[];
handler(params: { basePath: string; uiConfig: UIConfig }): ViewHandlerReturnType;
}
export type AppRouteDefs = {
entryPoint: AppViewRoute;
api: AppControllerRoute[];
};
export interface IServerAdapter {
setQueues(bullBoardQueues: BullBoardQueues): IServerAdapter;
setViewsPath(viewPath: string): IServerAdapter;
setStaticPath(staticsRoute: string, staticsPath: string): IServerAdapter;
setEntryRoute(route: AppViewRoute): IServerAdapter;
setErrorHandler(handler: (error: Error) => ControllerHandlerReturnType): IServerAdapter;
setApiRoutes(routes: AppControllerRoute[]): IServerAdapter;
setUIConfig(config: UIConfig): IServerAdapter;
}
export interface Pagination {
pageCount: number;
range: {
start: number;
end: number;
};
}
export type FormatterField = 'data' | 'returnValue' | 'name' | 'progress';
export type BoardOptions = {
uiBasePath?: string;
uiConfig?: UIConfig;
historyProvider?: MetricsHistoryProvider;
};
export type IMiscLink = {
text: string;
url: string;
};
export type UIConfig = Partial<{
boardTitle: string;
boardLogo: { path: string; width?: number | string; height?: number | string };
miscLinks: Array<IMiscLink>;
/** Hide the header Docs icon that links to the bull-board documentation site. Default: false (shown). */
hideDocsLink: boolean;
queueSortOptions: Array<{ key: string; label: string }>;
favIcon: FavIcon;
locale: { lng?: string };
dateFormats?: DateFormats;
pollingInterval?: Partial<{
showSetting: boolean;
forceInterval: number;
}>;
menu?: { width?: string };
overview?: { groupByDelimiter?: boolean };
sortQueues?: boolean;
hideRedisDetails?: boolean;
showMetrics?: boolean;
/** Set by createBullBoard when a historyProvider is configured. Enables the history range selector in the UI. */
hasHistoryProvider?: boolean;
/** Set by createBullBoard when the provider reports storage usage. Enables the storage panel. */
hasHistoryUsage?: boolean;
/** Set by createBullBoard when the provider can purge and the board is not read-only. */
canPurgeHistory?: boolean;
environment?: {
label: string;
color: string;
textColor?: string;
fontSize?: string | number;
};
}>;
export type FavIcon = {
default: string;
alternative: string;
};
export type DateFormats = {
/**
* When timestamp is in same day (today)
*
* @example `{ hour: 'numeric', minute: 'numeric', second: 'numeric' }`
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
*/
short?: Intl.DateTimeFormatOptions;
/**
* When timestamp is in same year
*
* @example `{ month: 'numeric', day: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' }`
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
*/
common?: Intl.DateTimeFormatOptions;
/**
* @example `{ year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' }`
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
*/
full?: Intl.DateTimeFormatOptions;
};