diff --git a/deepfence_frontend/apps/dashboard/api-spec.json b/deepfence_frontend/apps/dashboard/api-spec.json index 95b400cb75..3ab79d2ffc 100644 --- a/deepfence_frontend/apps/dashboard/api-spec.json +++ b/deepfence_frontend/apps/dashboard/api-spec.json @@ -721,6 +721,58 @@ "security": [{ "bearer_token": [] }] } }, + "/deepfence/database/vulnerability": { + "put": { + "tags": ["Settings"], + "summary": "Upload Vulnerability Database", + "description": "Upload Vulnerability Database for use in vulnerability scans", + "operationId": "uploadVulnerabilityDatabase", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { "$ref": "#/components/schemas/FormDataModelDBUploadRequest" } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ModelMessageResponse" } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsBadRequestResponse" } + } + } + }, + "401": { "description": "Unauthorized" }, + "403": { "description": "Forbidden" }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + } + }, + "security": [{ "bearer_token": [] }] + } + }, "/deepfence/diagnosis/agent-logs": { "post": { "tags": ["Diagnosis"], @@ -5395,6 +5447,152 @@ "security": [{ "bearer_token": [] }] } }, + "/deepfence/scans/bulk/delete": { + "post": { + "tags": ["Scan Results"], + "summary": "Bulk Delete Scans", + "description": "Bulk delete scans along with their results for a particular scan type", + "operationId": "bulkDeleteScans", + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ModelBulkDeleteScansRequest" } + } + } + }, + "responses": { + "200": { "description": "OK" }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsBadRequestResponse" } + } + } + }, + "401": { "description": "Unauthorized" }, + "403": { "description": "Forbidden" }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + } + }, + "security": [{ "bearer_token": [] }] + } + }, + "/deepfence/scheduled-task": { + "get": { + "tags": ["Settings"], + "summary": "Get scheduled tasks", + "description": "Get scheduled tasks", + "operationId": "getScheduledTasks", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/PostgresqlDbScheduler" } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsBadRequestResponse" } + } + } + }, + "401": { "description": "Unauthorized" }, + "403": { "description": "Forbidden" }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + } + }, + "security": [{ "bearer_token": [] }] + } + }, + "/deepfence/scheduled-task/{id}": { + "patch": { + "tags": ["Settings"], + "summary": "Update scheduled task", + "description": "Update scheduled task", + "operationId": "updateScheduledTask", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "integer" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ModelUpdateScheduledTaskRequest" } + } + } + }, + "responses": { + "204": { "description": "No Content" }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsBadRequestResponse" } + } + } + }, + "401": { "description": "Unauthorized" }, + "403": { "description": "Forbidden" }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + } + }, + "security": [{ "bearer_token": [] }] + } + }, "/deepfence/search/cloud-compliance/scans": { "post": { "tags": ["Search"], @@ -8303,6 +8501,13 @@ "node_type": { "enum": ["host", "cluster"], "type": "string" } } }, + "FormDataModelDBUploadRequest": { + "required": ["database"], + "type": "object", + "properties": { + "database": { "$ref": "#/components/schemas/FormDataMultipartFile" } + } + }, "FormDataModelRegistryGCRAddReq": { "required": ["name", "registry_url", "service_account_json"], "type": "object", @@ -8535,7 +8740,7 @@ "access_level": { "type": "string" }, "account_id": { "type": "string" }, "action": { "type": "string" }, - "allow_blob_public_access": { "type": "string" }, + "allow_blob_public_access": { "type": "boolean" }, "arn": { "type": "string" }, "attached_policy_arns": {}, "block_public_acls": { "type": "boolean" }, @@ -8897,6 +9102,23 @@ "node_type": { "type": "string" } } }, + "ModelBulkDeleteScansRequest": { + "required": ["scan_type", "filters"], + "type": "object", + "properties": { + "filters": { "$ref": "#/components/schemas/ReportersFieldsFilters" }, + "scan_type": { + "enum": [ + "Secret", + "Vulnerability", + "Malware", + "Compliance", + "CloudCompliance" + ], + "type": "string" + } + } + }, "ModelCloudCompliance": { "required": [ "count", @@ -10424,6 +10646,11 @@ "tags": { "type": "integer" } } }, + "ModelUpdateScheduledTaskRequest": { + "required": ["is_enabled"], + "type": "object", + "properties": { "is_enabled": { "type": "boolean" } } + }, "ModelUpdateUserIdRequest": { "type": "object", "properties": { @@ -10639,6 +10866,22 @@ "user_role_id": { "type": "integer" } } }, + "PostgresqlDbScheduler": { + "type": "object", + "properties": { + "action": { "type": "string" }, + "created_at": { "type": "string", "format": "date-time" }, + "cron_expr": { "type": "string" }, + "description": { "type": "string" }, + "id": { "type": "integer" }, + "is_enabled": { "type": "boolean" }, + "is_system": { "type": "boolean" }, + "last_ran_at": { "$ref": "#/components/schemas/SqlNullTime" }, + "payload": {}, + "status": { "type": "string" }, + "updated_at": { "type": "string", "format": "date-time" } + } + }, "ReportIDList": { "type": "array", "items": { "type": "string" } }, "ReportMetadata": { "type": "object", @@ -10851,6 +11094,7 @@ "window": { "$ref": "#/components/schemas/ModelFetchWindow" } } }, + "SqlNullTime": { "type": "object" }, "UtilsAdvancedReportFilters": { "type": "object", "properties": { diff --git a/deepfence_frontend/apps/dashboard/src/api/api.ts b/deepfence_frontend/apps/dashboard/src/api/api.ts index 2abf070b19..df7a8fb86c 100644 --- a/deepfence_frontend/apps/dashboard/src/api/api.ts +++ b/deepfence_frontend/apps/dashboard/src/api/api.ts @@ -296,5 +296,7 @@ export function getSettingsApiClient() { getEmailConfiguration: settingsApi.getEmailConfiguration.bind(settingsApi), addEmailConfiguration: settingsApi.addEmailConfiguration.bind(settingsApi), deleteEmailConfiguration: settingsApi.deleteEmailConfiguration.bind(settingsApi), + getScheduledTasks: settingsApi.getScheduledTasks.bind(settingsApi), + updateScheduledTask: settingsApi.updateScheduledTask.bind(settingsApi), }; } diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/apis/SettingsApi.ts b/deepfence_frontend/apps/dashboard/src/api/generated/apis/SettingsApi.ts index 10275ea953..0f67050e65 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/apis/SettingsApi.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/apis/SettingsApi.ts @@ -22,7 +22,9 @@ import type { ModelMessageResponse, ModelSettingUpdateRequest, ModelSettingsResponse, + ModelUpdateScheduledTaskRequest, PostgresqlDbGetAuditLogsRow, + PostgresqlDbScheduler, } from '../models'; import { ApiDocsBadRequestResponseFromJSON, @@ -39,8 +41,12 @@ import { ModelSettingUpdateRequestToJSON, ModelSettingsResponseFromJSON, ModelSettingsResponseToJSON, + ModelUpdateScheduledTaskRequestFromJSON, + ModelUpdateScheduledTaskRequestToJSON, PostgresqlDbGetAuditLogsRowFromJSON, PostgresqlDbGetAuditLogsRowToJSON, + PostgresqlDbSchedulerFromJSON, + PostgresqlDbSchedulerToJSON, } from '../models'; export interface AddEmailConfigurationRequest { @@ -51,11 +57,20 @@ export interface DeleteEmailConfigurationRequest { configId: string; } +export interface UpdateScheduledTaskRequest { + id: number; + modelUpdateScheduledTaskRequest?: ModelUpdateScheduledTaskRequest; +} + export interface UpdateSettingRequest { id: number; modelSettingUpdateRequest?: ModelSettingUpdateRequest; } +export interface UploadVulnerabilityDatabaseRequest { + database: Blob | null; +} + /** * SettingsApi - interface * @@ -110,6 +125,21 @@ export interface SettingsApiInterface { */ getEmailConfiguration(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + /** + * Get scheduled tasks + * @summary Get scheduled tasks + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SettingsApiInterface + */ + getScheduledTasksRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>>; + + /** + * Get scheduled tasks + * Get scheduled tasks + */ + getScheduledTasks(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + /** * Get all settings * @summary Get settings @@ -140,6 +170,23 @@ export interface SettingsApiInterface { */ getUserActivityLogs(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + /** + * Update scheduled task + * @summary Update scheduled task + * @param {number} id + * @param {ModelUpdateScheduledTaskRequest} [modelUpdateScheduledTaskRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SettingsApiInterface + */ + updateScheduledTaskRaw(requestParameters: UpdateScheduledTaskRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Update scheduled task + * Update scheduled task + */ + updateScheduledTask(requestParameters: UpdateScheduledTaskRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + /** * Update setting * @summary Update setting @@ -157,6 +204,22 @@ export interface SettingsApiInterface { */ updateSetting(requestParameters: UpdateSettingRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + /** + * Upload Vulnerability Database for use in vulnerability scans + * @summary Upload Vulnerability Database + * @param {Blob} database + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SettingsApiInterface + */ + uploadVulnerabilityDatabaseRaw(requestParameters: UploadVulnerabilityDatabaseRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Upload Vulnerability Database for use in vulnerability scans + * Upload Vulnerability Database + */ + uploadVulnerabilityDatabase(requestParameters: UploadVulnerabilityDatabaseRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + } /** @@ -278,6 +341,42 @@ export class SettingsApi extends runtime.BaseAPI implements SettingsApiInterface return await response.value(); } + /** + * Get scheduled tasks + * Get scheduled tasks + */ + async getScheduledTasksRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("bearer_token", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/deepfence/scheduled-task`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(PostgresqlDbSchedulerFromJSON)); + } + + /** + * Get scheduled tasks + * Get scheduled tasks + */ + async getScheduledTasks(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.getScheduledTasksRaw(initOverrides); + return await response.value(); + } + /** * Get all settings * Get settings @@ -350,6 +449,48 @@ export class SettingsApi extends runtime.BaseAPI implements SettingsApiInterface return await response.value(); } + /** + * Update scheduled task + * Update scheduled task + */ + async updateScheduledTaskRaw(requestParameters: UpdateScheduledTaskRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.id === null || requestParameters.id === undefined) { + throw new runtime.RequiredError('id','Required parameter requestParameters.id was null or undefined when calling updateScheduledTask.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("bearer_token", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/deepfence/scheduled-task/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters.id))), + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: ModelUpdateScheduledTaskRequestToJSON(requestParameters.modelUpdateScheduledTaskRequest), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Update scheduled task + * Update scheduled task + */ + async updateScheduledTask(requestParameters: UpdateScheduledTaskRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.updateScheduledTaskRaw(requestParameters, initOverrides); + } + /** * Update setting * Update setting @@ -392,4 +533,65 @@ export class SettingsApi extends runtime.BaseAPI implements SettingsApiInterface await this.updateSettingRaw(requestParameters, initOverrides); } + /** + * Upload Vulnerability Database for use in vulnerability scans + * Upload Vulnerability Database + */ + async uploadVulnerabilityDatabaseRaw(requestParameters: UploadVulnerabilityDatabaseRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.database === null || requestParameters.database === undefined) { + throw new runtime.RequiredError('database','Required parameter requestParameters.database was null or undefined when calling uploadVulnerabilityDatabase.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("bearer_token", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const consumes: runtime.Consume[] = [ + { contentType: 'multipart/form-data' }, + ]; + // @ts-ignore: canConsumeForm may be unused + const canConsumeForm = runtime.canConsumeForm(consumes); + + let formParams: { append(param: string, value: any): any }; + let useForm = false; + // use FormData to transmit files using content-type "multipart/form-data" + useForm = canConsumeForm; + if (useForm) { + formParams = new FormData(); + } else { + formParams = new URLSearchParams(); + } + + if (requestParameters.database !== undefined) { + formParams.append('database', requestParameters.database as any); + } + + const response = await this.request({ + path: `/deepfence/database/vulnerability`, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: formParams, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelMessageResponseFromJSON(jsonValue)); + } + + /** + * Upload Vulnerability Database for use in vulnerability scans + * Upload Vulnerability Database + */ + async uploadVulnerabilityDatabase(requestParameters: UploadVulnerabilityDatabaseRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.uploadVulnerabilityDatabaseRaw(requestParameters, initOverrides); + return await response.value(); + } + } diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelUpdateScheduledTaskRequest.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelUpdateScheduledTaskRequest.ts new file mode 100644 index 0000000000..8371de06cc --- /dev/null +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelUpdateScheduledTaskRequest.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Deepfence ThreatMapper + * Deepfence Runtime API provides programmatic control over Deepfence microservice securing your container, kubernetes and cloud deployments. The API abstracts away underlying infrastructure details like cloud provider, container distros, container orchestrator and type of deployment. This is one uniform API to manage and control security alerts, policies and response to alerts for microservices running anywhere i.e. managed pure greenfield container deployments or a mix of containers, VMs and serverless paradigms like AWS Fargate. + * + * The version of the OpenAPI document: 2.0.0 + * Contact: community@deepfence.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelUpdateScheduledTaskRequest + */ +export interface ModelUpdateScheduledTaskRequest { + /** + * + * @type {boolean} + * @memberof ModelUpdateScheduledTaskRequest + */ + is_enabled: boolean; +} + +/** + * Check if a given object implements the ModelUpdateScheduledTaskRequest interface. + */ +export function instanceOfModelUpdateScheduledTaskRequest(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "is_enabled" in value; + + return isInstance; +} + +export function ModelUpdateScheduledTaskRequestFromJSON(json: any): ModelUpdateScheduledTaskRequest { + return ModelUpdateScheduledTaskRequestFromJSONTyped(json, false); +} + +export function ModelUpdateScheduledTaskRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelUpdateScheduledTaskRequest { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'is_enabled': json['is_enabled'], + }; +} + +export function ModelUpdateScheduledTaskRequestToJSON(value?: ModelUpdateScheduledTaskRequest | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'is_enabled': value.is_enabled, + }; +} + diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/PostgresqlDbScheduler.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/PostgresqlDbScheduler.ts new file mode 100644 index 0000000000..548b797316 --- /dev/null +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/PostgresqlDbScheduler.ts @@ -0,0 +1,145 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Deepfence ThreatMapper + * Deepfence Runtime API provides programmatic control over Deepfence microservice securing your container, kubernetes and cloud deployments. The API abstracts away underlying infrastructure details like cloud provider, container distros, container orchestrator and type of deployment. This is one uniform API to manage and control security alerts, policies and response to alerts for microservices running anywhere i.e. managed pure greenfield container deployments or a mix of containers, VMs and serverless paradigms like AWS Fargate. + * + * The version of the OpenAPI document: 2.0.0 + * Contact: community@deepfence.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface PostgresqlDbScheduler + */ +export interface PostgresqlDbScheduler { + /** + * + * @type {string} + * @memberof PostgresqlDbScheduler + */ + action?: string; + /** + * + * @type {Date} + * @memberof PostgresqlDbScheduler + */ + created_at?: Date; + /** + * + * @type {string} + * @memberof PostgresqlDbScheduler + */ + cron_expr?: string; + /** + * + * @type {string} + * @memberof PostgresqlDbScheduler + */ + description?: string; + /** + * + * @type {number} + * @memberof PostgresqlDbScheduler + */ + id?: number; + /** + * + * @type {boolean} + * @memberof PostgresqlDbScheduler + */ + is_enabled?: boolean; + /** + * + * @type {boolean} + * @memberof PostgresqlDbScheduler + */ + is_system?: boolean; + /** + * + * @type {object} + * @memberof PostgresqlDbScheduler + */ + last_ran_at?: object; + /** + * + * @type {any} + * @memberof PostgresqlDbScheduler + */ + payload?: any | null; + /** + * + * @type {string} + * @memberof PostgresqlDbScheduler + */ + status?: string; + /** + * + * @type {Date} + * @memberof PostgresqlDbScheduler + */ + updated_at?: Date; +} + +/** + * Check if a given object implements the PostgresqlDbScheduler interface. + */ +export function instanceOfPostgresqlDbScheduler(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function PostgresqlDbSchedulerFromJSON(json: any): PostgresqlDbScheduler { + return PostgresqlDbSchedulerFromJSONTyped(json, false); +} + +export function PostgresqlDbSchedulerFromJSONTyped(json: any, ignoreDiscriminator: boolean): PostgresqlDbScheduler { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'action': !exists(json, 'action') ? undefined : json['action'], + 'created_at': !exists(json, 'created_at') ? undefined : (new Date(json['created_at'])), + 'cron_expr': !exists(json, 'cron_expr') ? undefined : json['cron_expr'], + 'description': !exists(json, 'description') ? undefined : json['description'], + 'id': !exists(json, 'id') ? undefined : json['id'], + 'is_enabled': !exists(json, 'is_enabled') ? undefined : json['is_enabled'], + 'is_system': !exists(json, 'is_system') ? undefined : json['is_system'], + 'last_ran_at': !exists(json, 'last_ran_at') ? undefined : json['last_ran_at'], + 'payload': !exists(json, 'payload') ? undefined : json['payload'], + 'status': !exists(json, 'status') ? undefined : json['status'], + 'updated_at': !exists(json, 'updated_at') ? undefined : (new Date(json['updated_at'])), + }; +} + +export function PostgresqlDbSchedulerToJSON(value?: PostgresqlDbScheduler | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'action': value.action, + 'created_at': value.created_at === undefined ? undefined : (value.created_at.toISOString()), + 'cron_expr': value.cron_expr, + 'description': value.description, + 'id': value.id, + 'is_enabled': value.is_enabled, + 'is_system': value.is_system, + 'last_ran_at': value.last_ran_at, + 'payload': value.payload, + 'status': value.status, + 'updated_at': value.updated_at === undefined ? undefined : (value.updated_at.toISOString()), + }; +} + diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts index 8a9cf3338d..8144b19437 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts @@ -128,6 +128,7 @@ export * from './ModelSecretScanTriggerReq'; export * from './ModelSettingUpdateRequest'; export * from './ModelSettingsResponse'; export * from './ModelSummary'; +export * from './ModelUpdateScheduledTaskRequest'; export * from './ModelUpdateUserIdRequest'; export * from './ModelUpdateUserPasswordRequest'; export * from './ModelUpdateUserRequest'; @@ -138,6 +139,7 @@ export * from './ModelVulnerabilityScanConfigLanguage'; export * from './ModelVulnerabilityScanResult'; export * from './ModelVulnerabilityScanTriggerReq'; export * from './PostgresqlDbGetAuditLogsRow'; +export * from './PostgresqlDbScheduler'; export * from './ReportMetadata'; export * from './ReportRawReport'; export * from './ReportersCompareFilter'; diff --git a/deepfence_frontend/apps/dashboard/src/features/settings/components/SettingsTab.tsx b/deepfence_frontend/apps/dashboard/src/features/settings/components/SettingsTab.tsx index 3eae41bf32..d43d55adc7 100644 --- a/deepfence_frontend/apps/dashboard/src/features/settings/components/SettingsTab.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/settings/components/SettingsTab.tsx @@ -1,4 +1,5 @@ import { + HiClock, HiCode, HiDocumentSearch, HiGlobeAlt, @@ -18,7 +19,8 @@ type RouteProps = | 'schedule-jobs' | 'scan-type-data-upload' | 'connection-instructions' - | 'global-settings'; + | 'global-settings' + | 'scheduled-jobs'; type SettingsTabProps = { children: React.ReactNode; value: RouteProps; @@ -33,6 +35,11 @@ export const settingsTabs: Array<{ value: 'user-management', icon: , }, + { + label: 'Scheduled Jobs', + value: 'scheduled-jobs', + icon: , + }, { label: 'Diagnostic Logs', value: 'diagnostic-logs', diff --git a/deepfence_frontend/apps/dashboard/src/features/settings/pages/ScheduledJobs.tsx b/deepfence_frontend/apps/dashboard/src/features/settings/pages/ScheduledJobs.tsx new file mode 100644 index 0000000000..f3f4c87c44 --- /dev/null +++ b/deepfence_frontend/apps/dashboard/src/features/settings/pages/ScheduledJobs.tsx @@ -0,0 +1,274 @@ +import { Suspense, useMemo } from 'react'; +import { IconContext } from 'react-icons'; +import { FaEye, FaEyeSlash } from 'react-icons/fa'; +import { HiClock, HiDotsVertical } from 'react-icons/hi'; +import { ActionFunctionArgs, useFetcher, useLoaderData } from 'react-router-dom'; +import { toast } from 'sonner'; +import { + Button, + createColumnHelper, + Dropdown, + DropdownItem, + Table, + TableSkeleton, +} from 'ui-components'; + +import { getSettingsApiClient } from '@/api/api'; +import { ApiDocsBadRequestResponse, PostgresqlDbScheduler } from '@/api/generated'; +import { SettingsTab } from '@/features/settings/components/SettingsTab'; +import { ApiError, makeRequest } from '@/utils/api'; +import { formatMilliseconds } from '@/utils/date'; +import { typedDefer, TypedDeferredData } from '@/utils/router'; +import { DFAwait } from '@/utils/suspense'; + +type LoaderDataType = { + message?: string; + data?: PostgresqlDbScheduler[]; +}; +const getData = async (): Promise => { + const response = await makeRequest({ + apiFunction: getSettingsApiClient().getScheduledTasks, + apiArgs: [], + }); + + if (ApiError.isApiError(response)) { + return { + message: 'Error in getting Scheduled Jobs list', + }; + } + + return { + data: response, + }; +}; +const loader = async (): Promise> => { + return typedDefer({ + data: getData(), + }); +}; + +export type ActionReturnType = { + message?: string; + success: boolean; +}; + +export const action = async ({ + request, +}: ActionFunctionArgs): Promise => { + const formData = await request.formData(); + const body = Object.fromEntries(formData); + const id = Number(body.id); + const isEnabled = body.isEnabled === 'true'; + + const r = await makeRequest({ + apiFunction: getSettingsApiClient().updateScheduledTask, + apiArgs: [ + { + id, + modelUpdateScheduledTaskRequest: { + is_enabled: isEnabled, + }, + }, + ], + errorHandler: async (r) => { + const error = new ApiError({ success: false }); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + message: modelResponse.message ?? '', + success: false, + }); + } + }, + }); + + if (ApiError.isApiError(r)) { + return r.value(); + } + + toast('Scheduled job status updated sucessfully'); + return { + success: true, + }; +}; +const ActionDropdown = ({ scheduler }: { scheduler: PostgresqlDbScheduler }) => { + const fetcher = useFetcher(); + const toggle = (scheduler: PostgresqlDbScheduler) => { + if (scheduler.id) { + const formData = new FormData(); + formData.append('id', scheduler.id?.toString() ?? ''); + formData.append('isEnabled', (!scheduler.is_enabled)?.toString() ?? ''); + fetcher.submit(formData, { + method: 'post', + }); + } + }; + return ( + <> + { + toggle(scheduler); + }} + > + + + {scheduler.is_enabled ? : } + + {scheduler.is_enabled ? 'Disable' : 'Enable'} + + + } + > + + + + ); +}; + +const ScheduledJobs = () => { + const columnHelper = createColumnHelper(); + const loaderData = useLoaderData() as LoaderDataType; + const columns = useMemo(() => { + const columns = [ + columnHelper.accessor('created_at', { + cell: (cell) => formatMilliseconds(cell.getValue() || ''), + header: () => 'Timestamp', + minSize: 30, + size: 50, + maxSize: 60, + }), + columnHelper.accessor('action', { + cell: (cell) => cell.getValue(), + header: () => 'Action', + minSize: 30, + size: 50, + maxSize: 85, + }), + columnHelper.accessor('description', { + cell: (cell) => cell.getValue(), + header: () => 'Description', + minSize: 30, + size: 90, + maxSize: 100, + }), + columnHelper.accessor('payload', { + cell: (cell) => cell.row.original.payload.node_type, + header: () => 'Node Type', + minSize: 30, + size: 40, + maxSize: 85, + }), + columnHelper.accessor('cron_expr', { + cell: (cell) => cell.getValue(), + header: () => 'Cron Expression', + minSize: 30, + size: 40, + maxSize: 85, + }), + columnHelper.accessor('is_enabled', { + cell: (cell) => + cell.getValue() ? ( + Yes + ) : ( + No + ), + header: () => 'Enabled', + minSize: 30, + size: 30, + maxSize: 85, + }), + columnHelper.accessor('status', { + cell: (cell) => cell.getValue(), + header: () => 'Status', + minSize: 30, + size: 30, + maxSize: 85, + }), + columnHelper.display({ + id: 'actions', + enableSorting: false, + cell: (cell) => { + if (!cell.row.original.id) { + throw new Error('Scheduled job id not found'); + } + return ; + }, + header: () => '', + minSize: 20, + size: 20, + maxSize: 20, + enableResizing: false, + }), + ]; + return columns; + }, []); + + return ( + +
+
+
+
+
+
+ + + +
+

+ Scheduled Jobs +

+
+
+
+ } + > + + {(resolvedData: LoaderDataType) => { + const { data, message } = resolvedData; + const list = data ?? []; + return ( +
+ {message ? ( +

{message}

+ ) : ( + + )} + + ); + }} + + + + + + ); +}; + +export const module = { + element: , + loader, + action, +}; diff --git a/deepfence_frontend/apps/dashboard/src/routes/private.tsx b/deepfence_frontend/apps/dashboard/src/routes/private.tsx index a6c156588c..a467a4a9fb 100644 --- a/deepfence_frontend/apps/dashboard/src/routes/private.tsx +++ b/deepfence_frontend/apps/dashboard/src/routes/private.tsx @@ -71,6 +71,7 @@ import { module as connectorInstructions } from '@/features/settings/pages/Conne import { module as diagnosticLogs } from '@/features/settings/pages/DiagnosticLogs'; import { module as emailConfiguration } from '@/features/settings/pages/EmailConfiguration'; import { module as globalSettings } from '@/features/settings/pages/GlobalSettings'; +import { module as scheduledJobs } from '@/features/settings/pages/ScheduledJobs'; import { module as settings } from '@/features/settings/pages/Settings'; import { module as userAuditLogs } from '@/features/settings/pages/UserAuditLogs'; import { module as userManagement } from '@/features/settings/pages/UserManagement'; @@ -501,6 +502,12 @@ export const privateRoutes: CustomRouteObject[] = [ ...userManagement, meta: { title: 'User Management' }, }, + { + path: 'scheduled-jobs', + ...scheduledJobs, + meta: { title: 'Scheduled Jobs' }, + }, + { path: 'user-audit-logs', ...userAuditLogs,