diff --git a/backend/src/data-source.ts b/backend/src/data-source.ts index a2c9da57..0dc66241 100644 --- a/backend/src/data-source.ts +++ b/backend/src/data-source.ts @@ -22,6 +22,7 @@ import { addUniqueConstraintApiEndpoint1666678487137 } from "migrations/16666784 import { dropAnalyzedColumnFromApiTrace1666752646836 } from "migrations/1666752646836-drop-analyzed-column-from-api-trace" import { addIndexForDataField1666941075032 } from "migrations/1666941075032-add-index-for-data-field" import { addIsgraphqlColumnApiEndpoint1667095325334 } from "migrations/1667095325334-add-isgraphql-column-api-endpoint" +import { addApiEndpointUuidIndexForAlert1667259254414 } from "migrations/1667259254414-add-apiEndpointUuid-index-for-alert" export const AppDataSource: DataSource = new DataSource({ type: "postgres", @@ -49,6 +50,7 @@ export const AppDataSource: DataSource = new DataSource({ dropAnalyzedColumnFromApiTrace1666752646836, addIndexForDataField1666941075032, addIsgraphqlColumnApiEndpoint1667095325334, + addApiEndpointUuidIndexForAlert1667259254414, ], migrationsRun: runMigration, logging: false, diff --git a/backend/src/jobs.ts b/backend/src/jobs.ts index 80fd9bf9..17e153fc 100644 --- a/backend/src/jobs.ts +++ b/backend/src/jobs.ts @@ -9,6 +9,12 @@ import { } from "services/jobs" import runAllTests from "services/testing/runAllTests" import { logAggregatedStats } from "services/logging" +import { DateTime } from "luxon" + +const log = (logMessage: string, newLine?: boolean) => + console.log( + `${newLine ? "\n" : ""}${DateTime.utc().toString()} ${logMessage}`, + ) const main = async () => { const datasource = await AppDataSource.initialize() @@ -25,20 +31,20 @@ const main = async () => { const logAggregateStatsSem = semaphore(1) const checkForUnauthenticatedSem = semaphore(1) - schedule.scheduleJob("30 * * * *", () => { + schedule.scheduleJob("*/10 * * * *", () => { generateSpecSem.take(async () => { - console.log("\nGenerating OpenAPI Spec Files...") + log("Generating OpenAPI Spec Files...", true) await generateOpenApiSpec() - console.log("Finished generating OpenAPI Spec Files.") + log("Finished generating OpenAPI Spec Files.") generateSpecSem.leave() }) }) schedule.scheduleJob("30 * * * * ", () => { checkForUnauthenticatedSem.take(async () => { - console.log("\nChecking for Unauthenticated Endpoints") + log("Checking for Unauthenticated Endpoints", true) await checkForUnauthenticatedEndpoints() - console.log("Finished checking for Unauthenticated Endpoints") + log("Finished checking for Unauthenticated Endpoints") checkForUnauthenticatedSem.leave() }) }) @@ -46,27 +52,27 @@ const main = async () => { // Offset by 15 minutes past every 4th hour, so that there isn't any excess database slowdown schedule.scheduleJob("15 * * * *", () => { unsecuredAlertsSem.take(async () => { - console.log("\nGenerating Alerts for Unsecured Endpoints") + log("Generating Alerts for Unsecured Endpoints", true) await monitorEndpointForHSTS() - console.log("Finished generating alerts for Unsecured Endpoints.") + log("Finished generating alerts for Unsecured Endpoints.") unsecuredAlertsSem.leave() }) }) schedule.scheduleJob("30 * * * *", () => { testsSem.take(async () => { - console.log("\nRunning Tests...") + log("Running Tests...", true) await runAllTests() - console.log("Finished running tests.") + log("Finished running tests.") testsSem.leave() }) }) schedule.scheduleJob("*/10 * * * *", () => { clearApiTracesSem.take(async () => { - console.log("\nClearing Api Trace data...") + log("Clearing Api Trace data...", true) await clearApiTraces() - console.log("Finished clearing Api Trace data.") + log("Finished clearing Api Trace data.") clearApiTracesSem.leave() }) }) @@ -74,14 +80,14 @@ const main = async () => { if ((process.env.DISABLE_LOGGING_STATS || "false").toLowerCase() == "false") { schedule.scheduleJob("0 */6 * * *", () => { logAggregateStatsSem.take(async () => { - console.log("\nLogging Aggregated Stats...") + log("Logging Aggregated Stats...", true) await logAggregatedStats() - console.log("Finished Logging Aggregated Stats.") + log("Finished Logging Aggregated Stats.") logAggregateStatsSem.leave() }) }) } else { - console.log("\nLogging Aggregated Stats Disabled...") + log("Logging Aggregated Stats Disabled...", true) } process.on("SIGINT", () => { diff --git a/backend/src/migrations/1667259254414-add-apiEndpointUuid-index-for-alert.ts b/backend/src/migrations/1667259254414-add-apiEndpointUuid-index-for-alert.ts new file mode 100644 index 00000000..c7a63aeb --- /dev/null +++ b/backend/src/migrations/1667259254414-add-apiEndpointUuid-index-for-alert.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class addApiEndpointUuidIndexForAlert1667259254414 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE INDEX IF NOT EXISTS "apiEndpointUuid_alert" ON "alert" ("apiEndpointUuid")`, + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX IF EXISTS "apiEndpointUuid_alert"`) + } +} diff --git a/backend/src/models/alert.ts b/backend/src/models/alert.ts index 13794cf8..87940e08 100644 --- a/backend/src/models/alert.ts +++ b/backend/src/models/alert.ts @@ -3,6 +3,7 @@ import { Column, CreateDateColumn, Entity, + Index, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn, @@ -27,6 +28,7 @@ export class Alert extends BaseEntity { riskScore: RiskScore @Column() + @Index("apiEndpointUuid_alert") apiEndpointUuid: string @ManyToOne(() => ApiEndpoint, apiEndpoint => apiEndpoint.alerts) diff --git a/backend/src/services/get-endpoints/index.ts b/backend/src/services/get-endpoints/index.ts index 975c2571..383fe706 100644 --- a/backend/src/services/get-endpoints/index.ts +++ b/backend/src/services/get-endpoints/index.ts @@ -4,6 +4,9 @@ import { ApiEndpointTest, ApiTrace, AggregateTraceDataHourly, + Alert, + DataField, + OpenApiSpec, } from "models" import { GetEndpointParams, @@ -17,6 +20,28 @@ import Error404NotFound from "errors/error-404-not-found" import { getRiskScore } from "utils" import { getEndpointsCountQuery, getEndpointsQuery } from "./queries" +const GET_DATA_FIELDS_QUERY = ` +SELECT + uuid, + "dataClasses"::text[], + "falsePositives"::text[], + "scannerIdentified"::text[], + "dataType", + "dataTag", + "dataSection", + "createdAt", + "updatedAt", + "dataPath", + "apiEndpointUuid" +FROM + data_field +WHERE + "apiEndpointUuid" = $1 +ORDER BY + "dataTag" ASC, + "dataPath" ASC +` + export class GetEndpointsService { static async updateIsAuthenticated( apiEndpointUuid: string, @@ -118,29 +143,29 @@ export class GetEndpointsService { const queryRunner = AppDataSource.createQueryRunner() try { await queryRunner.connect() - const endpoint = await queryRunner.manager.findOne(ApiEndpoint, { - select: { - alerts: { - uuid: true, - status: true, - }, - }, - where: { uuid: endpointId }, - relations: { - dataFields: true, - openapiSpec: true, - alerts: true, - }, - order: { - dataFields: { - dataTag: "ASC", - dataPath: "ASC", - }, - }, - }) + const endpoint = await queryRunner.manager + .createQueryBuilder() + .from(ApiEndpoint, "endpoint") + .where("uuid = :id", { id: endpointId }) + .getRawOne() if (!endpoint) { throw new Error404NotFound("Endpoint does not exist.") } + const alerts = await queryRunner.manager + .createQueryBuilder() + .select(["uuid", "status"]) + .from(Alert, "alert") + .where(`"apiEndpointUuid" = :id`, { id: endpointId }) + .getRawMany() + const dataFields: DataField[] = await queryRunner.query( + GET_DATA_FIELDS_QUERY, + [endpointId], + ) + const openapiSpec = await queryRunner.manager + .createQueryBuilder() + .from(OpenApiSpec, "spec") + .where("name = :name", { name: endpoint.openapiSpecName }) + .getRawOne() const traces = await queryRunner.manager.find(ApiTrace, { where: { apiEndpointUuid: endpoint.uuid }, order: { createdAt: "DESC" }, @@ -151,6 +176,9 @@ export class GetEndpointsService { }) return { ...endpoint, + alerts, + dataFields, + openapiSpec, traces: [...traces], tests: tests as Array, } diff --git a/backend/src/services/jobs/generate-openapi-spec.ts b/backend/src/services/jobs/generate-openapi-spec.ts index a34ce166..50807621 100644 --- a/backend/src/services/jobs/generate-openapi-spec.ts +++ b/backend/src/services/jobs/generate-openapi-spec.ts @@ -62,18 +62,23 @@ const generateOpenApiSpec = async (): Promise => { const paths = openApiSpec["paths"] const path = endpoint.path const method = endpoint.method.toLowerCase() - const tracesQb = apiTraceRepository + let tracesQb = apiTraceRepository .createQueryBuilder() .where('"apiEndpointUuid" = :id', { id: endpoint.uuid }) if (spec.specUpdatedAt) { - tracesQb.andWhere('"createdAt" > :updated', { - updated: spec.specUpdatedAt, - }) - tracesQb.andWhere('"createdAt" <= :curr', { curr: currTime }) + tracesQb = tracesQb + .andWhere('"createdAt" > :updated', { + updated: spec.specUpdatedAt, + }) + .andWhere('"createdAt" <= :curr', { curr: currTime }) } else { - tracesQb.andWhere('"createdAt" <= :curr', { curr: currTime }) + tracesQb = tracesQb.andWhere('"createdAt" <= :curr', { + curr: currTime, + }) } - const traces = await tracesQb.orderBy('"createdAt"', "ASC").getMany() + tracesQb = tracesQb.orderBy('"createdAt"', "ASC").limit(5000) + let traces = await tracesQb.getMany() + let offset = traces.length let parameters: Record = {} let requestBodySpec: BodyContent = {} @@ -96,83 +101,86 @@ const generateOpenApiSpec = async (): Promise => { [method]: {}, } } - - for (const trace of traces) { - const requestParamters = trace.requestParameters - const requestHeaders = trace.requestHeaders - const requestBody = trace.requestBody - const responseHeaders = trace.responseHeaders - const responseBody = trace.responseBody - const responseStatusString = - trace.responseStatus?.toString() || "default" - let requestContentType = null - let responseContentType = null - const endpointTokens = getPathTokens(endpoint.path) - const traceTokens = getPathTokens(trace.path) - for (let i = 0; i < endpointTokens.length; i++) { - const currToken = endpointTokens[i] - if (isParameter(currToken)) { - const key = `${currToken.slice(1, -1)}<>path` - parameters[key] = parseSchema( - parameters[key] ?? {}, - parsedJsonNonNull(traceTokens[i], true), - ) - } - } - if (trace.responseStatus < 400) { - for (const requestParameter of requestParamters) { - const key = `${requestParameter.name}<>query` - parameters[key] = parseSchema( - parameters[key] ?? {}, - parsedJsonNonNull(requestParameter.value, true), - ) - } - for (const requestHeader of requestHeaders) { - const key = `${requestHeader.name}<>header` - parameters[key] = parseSchema( - parameters[key] ?? {}, - parsedJsonNonNull(requestHeader.value, true), - ) - if (requestHeader.name.toLowerCase() === "content-type") { - requestContentType = requestHeader.value.toLowerCase() + while (traces?.length > 0) { + for (const trace of traces) { + const requestParamters = trace.requestParameters + const requestHeaders = trace.requestHeaders + const requestBody = trace.requestBody + const responseHeaders = trace.responseHeaders + const responseBody = trace.responseBody + const responseStatusString = + trace.responseStatus?.toString() || "default" + let requestContentType = null + let responseContentType = null + const endpointTokens = getPathTokens(endpoint.path) + const traceTokens = getPathTokens(trace.path) + for (let i = 0; i < endpointTokens.length; i++) { + const currToken = endpointTokens[i] + if (isParameter(currToken)) { + const key = `${currToken.slice(1, -1)}<>path` + parameters[key] = parseSchema( + parameters[key] ?? {}, + parsedJsonNonNull(traceTokens[i], true), + ) } } - } - for (const responseHeader of responseHeaders) { - if (responseHeader.name.toLowerCase() === "content-type") { - responseContentType = responseHeader.value.toLowerCase() + if (trace.responseStatus < 400) { + for (const requestParameter of requestParamters) { + const key = `${requestParameter.name}<>query` + parameters[key] = parseSchema( + parameters[key] ?? {}, + parsedJsonNonNull(requestParameter.value, true), + ) + } + for (const requestHeader of requestHeaders) { + const key = `${requestHeader.name}<>header` + parameters[key] = parseSchema( + parameters[key] ?? {}, + parsedJsonNonNull(requestHeader.value, true), + ) + if (requestHeader.name.toLowerCase() === "content-type") { + requestContentType = requestHeader.value.toLowerCase() + } + } } - if (!responses[responseStatusString]?.headers) { - responses[responseStatusString] = { - description: `${responseStatusString} description`, - ...responses[responseStatusString], - headers: {}, + for (const responseHeader of responseHeaders) { + if (responseHeader.name.toLowerCase() === "content-type") { + responseContentType = responseHeader.value.toLowerCase() } + if (!responses[responseStatusString]?.headers) { + responses[responseStatusString] = { + description: `${responseStatusString} description`, + ...responses[responseStatusString], + headers: {}, + } + } + parseContent( + responses[responseStatusString]?.headers, + responseHeader.value, + responseHeader.name, + ) } - parseContent( - responses[responseStatusString]?.headers, - responseHeader.value, - responseHeader.name, - ) - } - if (trace.responseStatus < 400) { - parseContent(requestBodySpec, requestBody, requestContentType) - } - if (responseBody) { - if (!responses[responseStatusString]?.content) { - responses[responseStatusString] = { - description: `${responseStatusString} description`, - ...responses[responseStatusString], - content: {}, + if (trace.responseStatus < 400) { + parseContent(requestBodySpec, requestBody, requestContentType) + } + if (responseBody) { + if (!responses[responseStatusString]?.content) { + responses[responseStatusString] = { + description: `${responseStatusString} description`, + ...responses[responseStatusString], + content: {}, + } } + parseContent( + responses[responseStatusString]?.content, + responseBody, + responseContentType, + ) } - parseContent( - responses[responseStatusString]?.content, - responseBody, - responseContentType, - ) } + traces = await tracesQb.offset(offset).getMany() + offset += traces.length } let specParameterList = [] for (const parameter in parameters) { diff --git a/backend/src/services/jobs/utils.ts b/backend/src/services/jobs/utils.ts index 64b16c0e..dc06a40d 100644 --- a/backend/src/services/jobs/utils.ts +++ b/backend/src/services/jobs/utils.ts @@ -44,8 +44,13 @@ export const parseSchema = (bodySchema: BodySchema, parsedBody: any) => { } return bodySchema } else if (dataType === DataType.UNKNOWN) { + if (bodySchema?.type) { + return { + type: bodySchema?.type, + nullable: true, + } + } return { - type: bodySchema?.type ?? DataType.UNKNOWN, nullable: true, } } else { diff --git a/backend/src/suricata_setup/gcp-services/gcp_setup.ts b/backend/src/suricata_setup/gcp-services/gcp_setup.ts index 28dbf501..aa455de9 100644 --- a/backend/src/suricata_setup/gcp-services/gcp_setup.ts +++ b/backend/src/suricata_setup/gcp-services/gcp_setup.ts @@ -840,7 +840,7 @@ export async function push_files({ id, instance_url, ...rest -}: RESPONSE["data"]): Promise { +}: RESPONSE["data"]): Promise { const instance_name = instance_url.split("/").at(-1) let [key, raw] = createApiKey(`Metlo-collector-${id}`) key.for = API_KEY_TYPE.GCP @@ -861,10 +861,7 @@ export async function push_files({ ) put_data_file( - format(filepath_ingestor_in, [ - `${process.env.BACKEND_URL}`, - raw, - ]), + format(filepath_ingestor_in, [`${process.env.BACKEND_URL}`, raw]), filepath_ingestor_out, ) put_data_file( diff --git a/frontend/src/components/Alert/AlertDetail.tsx b/frontend/src/components/Alert/AlertDetail.tsx index 951001d9..fb0522dc 100644 --- a/frontend/src/components/Alert/AlertDetail.tsx +++ b/frontend/src/components/Alert/AlertDetail.tsx @@ -16,8 +16,7 @@ import { AccordionPanel, Spinner, } from "@chakra-ui/react" -import lightTheme from "prism-react-renderer/themes/github" -import Highlight, { defaultProps } from "prism-react-renderer" +import Editor from "@monaco-editor/react" import { AlertType, SpecExtension, Status } from "@common/enums" import { Alert, ApiTrace, MinimizedSpecContext } from "@common/types" import { getDateTimeString } from "utils" @@ -99,70 +98,35 @@ const handleOpenApiSpec = ( {specString ? ( - - {({ className, style, tokens, getLineProps, getTokenProps }) => { - return ( -
-                  {tokens.map((line, i) => {
-                    const lineProps = getLineProps({ line, key: i })
-                    if (i + 1 === lineNumber) {
-                      lineProps.className = `${lineProps.className} highlight-line light`
-                    }
-                    return (
-                      
-                        
-                          {i + 1}
-                        
-                        
-                          {line.map((token, key) => (
-                            
-                          ))}
-                        
-                      
- ) - })} -
+ { + editor.revealLineInCenter(lineNumber) + editor.deltaDecorations( + [], + [ + { + range: new monaco.Range(lineNumber, 1, lineNumber, 1), + options: { + isWholeLine: true, + className: "highlight-line.light", + }, + }, + ], ) }} -
+ options={{ + minimap: { + enabled: false, + }, + automaticLayout: true, + readOnly: true, + renderIndentGuides: false, + scrollBeyondLastLine: false, + }} + /> ) : ( Related spec no longer exists or has been deleted. )} @@ -288,13 +252,13 @@ export const AlertDetail: React.FC = ({ } } setPanels() - }, []) + }, [alert, providedSpecExtension, providedSpecString]) useEffect(() => { if (showRightPanel(alert.type)) { executeScroll() } - }, [rightPanel]) + }, [rightPanel, alert]) return ( diff --git a/frontend/src/components/Endpoint/SpecComponent.tsx b/frontend/src/components/Endpoint/SpecComponent.tsx index 5b0a8470..df5b3a84 100644 --- a/frontend/src/components/Endpoint/SpecComponent.tsx +++ b/frontend/src/components/Endpoint/SpecComponent.tsx @@ -1,26 +1,15 @@ import { useState } from "react" -import { - Box, - useColorModeValue, - HStack, - Tag, - IconButton, - Select, - Tooltip, - Icon, -} from "@chakra-ui/react" -import { HiOutlineExclamationCircle } from "@react-icons/all-files/hi/HiOutlineExclamationCircle" +import { Box, HStack, Tag, IconButton, Select } from "@chakra-ui/react" +import Editor from "@monaco-editor/react" import { saveAs } from "file-saver" import { BsFileCode } from "@react-icons/all-files/bs/BsFileCode" import YAML from "yaml" import JsYaml from "js-yaml" -import Highlight, { defaultProps } from "prism-react-renderer" -import darkTheme from "prism-react-renderer/themes/duotoneDark" -import lightTheme from "prism-react-renderer/themes/github" import { FiDownload } from "@react-icons/all-files/fi/FiDownload" import { ApiEndpointDetailed } from "@common/types" import { SpecExtension } from "@common/enums" import { DataHeading } from "components/utils/Card" +import EmptyView from "components/utils/EmptyView" interface SpecComponentProps { endpoint: ApiEndpointDetailed @@ -32,7 +21,6 @@ const EXTENSION_TO_TYPE: Record = { } const SpecComponent: React.FC = ({ endpoint }) => { - const theme = useColorModeValue(lightTheme, darkTheme) const [specString, setSpecString] = useState( endpoint.openapiSpec?.spec, ) @@ -67,115 +55,78 @@ const SpecComponent: React.FC = ({ endpoint }) => { return ( - {endpoint?.openapiSpec && ( - - - + {endpoint?.openapiSpec ? ( + + + + + + + {endpoint?.openapiSpec?.name} + + - {endpoint?.openapiSpec?.isAutoGenerated ? ( - - - - ) : ( - + {endpoint?.openapiSpec?.isAutoGenerated && ( + + Generated + )} - - {endpoint?.openapiSpec?.name} - - - {endpoint?.openapiSpec?.isAutoGenerated && ( - - Generated - - )} - + + + } + /> + - - - } + + - - + + + ) : ( + )} - - - {({ className, style, tokens, getLineProps, getTokenProps }) => ( -
-              {tokens.map((line, i) => (
-                
-                  
-                    {i + 1}
-                  
-                  
-                    {line.map((token, key) => (
-                      
-                    ))}
-                  
-                
- ))} -
- )} -
-
) } diff --git a/frontend/src/components/Endpoint/TraceDetail.tsx b/frontend/src/components/Endpoint/TraceDetail.tsx index a8585b06..3ae13eb4 100644 --- a/frontend/src/components/Endpoint/TraceDetail.tsx +++ b/frontend/src/components/Endpoint/TraceDetail.tsx @@ -31,10 +31,18 @@ export const JSONContentViewer = ( ) => { const bgColor = colorMode === "dark" ? "#4C5564" : "#EDF2F7" try { - const parsedData = JSON.parse(data ?? "{}") - if (typeof parsedData !== "object" && !Array.isArray(parsedData)) { + let parsedData: object + if (!data) { throw new Error() } + if (typeof data === "object") { + parsedData = data + } else { + parsedData = JSON.parse(data ?? "{}") + if (typeof parsedData !== "object" && !Array.isArray(parsedData)) { + throw new Error() + } + } return ( - {data} + {`${data ?? ""}`} ) } diff --git a/frontend/src/components/Keys/list.tsx b/frontend/src/components/Keys/list.tsx index 1098c08f..bcb52bd5 100644 --- a/frontend/src/components/Keys/list.tsx +++ b/frontend/src/components/Keys/list.tsx @@ -130,8 +130,8 @@ const ListKeys: React.FC = ({ keys, setKeys }) => { Confirm Deletion of API Key - Confirm deletion of API Key : - + Confirm deletion of API Key : + {deletePromptKeyName} diff --git a/frontend/src/components/Spec/index.tsx b/frontend/src/components/Spec/index.tsx index ed411edd..2766c2e6 100644 --- a/frontend/src/components/Spec/index.tsx +++ b/frontend/src/components/Spec/index.tsx @@ -10,21 +10,19 @@ import { InputGroup, Button, useToast, + Box, } from "@chakra-ui/react" import { AiFillApi } from "@react-icons/all-files/ai/AiFillApi" -import darkTheme from "prism-react-renderer/themes/duotoneDark" -import lightTheme from "prism-react-renderer/themes/github" -import Highlight, { defaultProps } from "prism-react-renderer" import { OpenApiSpec } from "@common/types" import { deleteSpec, updateSpec } from "api/apiSpecs" import { makeToast } from "utils" +import Editor from "@monaco-editor/react" interface SpecPageProps { spec: OpenApiSpec } const SpecPage: React.FC = React.memo(({ spec }) => { - const theme = useColorModeValue(lightTheme, darkTheme) const inputRef = useRef(null) const router = useRouter() const toast = useToast() @@ -90,7 +88,7 @@ const SpecPage: React.FC = React.memo(({ spec }) => { ) return ( - + @@ -123,56 +121,22 @@ const SpecPage: React.FC = React.memo(({ spec }) => { - - {({ className, style, tokens, getLineProps, getTokenProps }) => ( -
-            {tokens.map((line, i) => (
-              
-                
-                  {i + 1}
-                
-                
-                  {line.map((token, key) => (
-                    
-                  ))}
-                
-              
- ))} -
- )} -
+ + +
) }) diff --git a/frontend/src/main.css b/frontend/src/main.css index 6df555a5..a9e658c4 100644 --- a/frontend/src/main.css +++ b/frontend/src/main.css @@ -63,4 +63,6 @@ .highlight-line.dark { background-color: var(--chakra-colors-orange-800); -} \ No newline at end of file +} + +.monaco-editor-overlaymessage { display: none !important; } diff --git a/frontend/src/pages/settings.tsx b/frontend/src/pages/settings.tsx index 033eca68..874419dd 100644 --- a/frontend/src/pages/settings.tsx +++ b/frontend/src/pages/settings.tsx @@ -48,14 +48,14 @@ const Keys = ({ keys: _keysString }) => { const addKey = async (key_name: string) => { setIsAddingKey(true) try { - let resp = await addKeyReq(key_name) + let resp = await addKeyReq(key_name) let new_keys = [...keys] new_keys.push({ name: resp.name, identifier: resp.identifier, created: resp.created, for: resp.for, - }) + }) setKeys(new_keys) setNewKeyValue([resp.apiKey, resp.name]) onNewKeyOpen() diff --git a/frontend/src/pages/spec/[name].tsx b/frontend/src/pages/spec/[name].tsx index 035d6a7c..9b477ca2 100644 --- a/frontend/src/pages/spec/[name].tsx +++ b/frontend/src/pages/spec/[name].tsx @@ -15,7 +15,7 @@ const Spec = ({ spec }) => { } return ( - +