Merge pull request #2760 from Infisical/daniel/request-ids

feat: request ID support
This commit is contained in:
Daniel Hougaard
2024-11-25 16:13:19 +04:00
committed by GitHub
20 changed files with 210 additions and 86 deletions
+10
View File
@@ -24,6 +24,7 @@
"@fastify/multipart": "8.3.0", "@fastify/multipart": "8.3.0",
"@fastify/passport": "^2.4.0", "@fastify/passport": "^2.4.0",
"@fastify/rate-limit": "^9.0.0", "@fastify/rate-limit": "^9.0.0",
"@fastify/request-context": "^5.1.0",
"@fastify/session": "^10.7.0", "@fastify/session": "^10.7.0",
"@fastify/swagger": "^8.14.0", "@fastify/swagger": "^8.14.0",
"@fastify/swagger-ui": "^2.1.0", "@fastify/swagger-ui": "^2.1.0",
@@ -5528,6 +5529,15 @@
"toad-cache": "^3.3.0" "toad-cache": "^3.3.0"
} }
}, },
"node_modules/@fastify/request-context": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@fastify/request-context/-/request-context-5.1.0.tgz",
"integrity": "sha512-PM7wrLJOEylVDpxabOFLaYsdAiaa0lpDUcP2HMFJ1JzgiWuC6k4r3duf6Pm9YLnzlGmT+Yp4tkQjqsu7V/pSOA==",
"license": "MIT",
"dependencies": {
"fastify-plugin": "^4.0.0"
}
},
"node_modules/@fastify/send": { "node_modules/@fastify/send": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz",
+1
View File
@@ -132,6 +132,7 @@
"@fastify/multipart": "8.3.0", "@fastify/multipart": "8.3.0",
"@fastify/passport": "^2.4.0", "@fastify/passport": "^2.4.0",
"@fastify/rate-limit": "^9.0.0", "@fastify/rate-limit": "^9.0.0",
"@fastify/request-context": "^5.1.0",
"@fastify/session": "^10.7.0", "@fastify/session": "^10.7.0",
"@fastify/swagger": "^8.14.0", "@fastify/swagger": "^8.14.0",
"@fastify/swagger-ui": "^2.1.0", "@fastify/swagger-ui": "^2.1.0",
+7
View File
@@ -0,0 +1,7 @@
import "@fastify/request-context";
declare module "@fastify/request-context" {
interface RequestContextData {
requestId: string;
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify"; import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify";
import { Logger } from "pino";
import { CustomLogger } from "@app/lib/logger/logger";
import { ZodTypeProvider } from "@app/server/plugins/fastify-zod"; import { ZodTypeProvider } from "@app/server/plugins/fastify-zod";
declare global { declare global {
@@ -8,7 +8,7 @@ declare global {
RawServerDefault, RawServerDefault,
RawRequestDefaultExpression<RawServerDefault>, RawRequestDefaultExpression<RawServerDefault>,
RawReplyDefaultExpression<RawServerDefault>, RawReplyDefaultExpression<RawServerDefault>,
Readonly<Logger>, Readonly<CustomLogger>,
ZodTypeProvider ZodTypeProvider
>; >;
+2 -2
View File
@@ -27,7 +27,7 @@ export const initializeHsmModule = () => {
logger.info("PKCS#11 module initialized"); logger.info("PKCS#11 module initialized");
} catch (err) { } catch (err) {
logger.error("Failed to initialize PKCS#11 module:", err); logger.error(err, "Failed to initialize PKCS#11 module");
throw err; throw err;
} }
}; };
@@ -39,7 +39,7 @@ export const initializeHsmModule = () => {
isInitialized = false; isInitialized = false;
logger.info("PKCS#11 module finalized"); logger.info("PKCS#11 module finalized");
} catch (err) { } catch (err) {
logger.error("Failed to finalize PKCS#11 module:", err); logger.error(err, "Failed to finalize PKCS#11 module");
throw err; throw err;
} }
} }
@@ -36,8 +36,7 @@ export const testLDAPConfig = async (ldapConfig: TLDAPConfig): Promise<boolean>
}); });
ldapClient.on("error", (err) => { ldapClient.on("error", (err) => {
logger.error("LDAP client error:", err); logger.error(err, "LDAP client error");
logger.error(err);
resolve(false); resolve(false);
}); });
@@ -161,8 +161,8 @@ export const licenseServiceFactory = ({
} }
} catch (error) { } catch (error) {
logger.error( logger.error(
`getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error]`, error,
error `getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error]`
); );
await keyStore.setItemWithExpiry( await keyStore.setItemWithExpiry(
FEATURE_CACHE_KEY(orgId), FEATURE_CACHE_KEY(orgId),
@@ -46,7 +46,7 @@ export const rateLimitServiceFactory = ({ rateLimitDAL, licenseService }: TRateL
} }
return rateLimit; return rateLimit;
} catch (err) { } catch (err) {
logger.error("Error fetching rate limits %o", err); logger.error(err, "Error fetching rate limits");
return undefined; return undefined;
} }
}; };
@@ -69,12 +69,12 @@ export const rateLimitServiceFactory = ({ rateLimitDAL, licenseService }: TRateL
mfaRateLimit: rateLimit.mfaRateLimit mfaRateLimit: rateLimit.mfaRateLimit
}; };
logger.info(`syncRateLimitConfiguration: rate limit configuration: %o`, newRateLimitMaxConfiguration); logger.info(newRateLimitMaxConfiguration, "syncRateLimitConfiguration: rate limit configuration");
Object.freeze(newRateLimitMaxConfiguration); Object.freeze(newRateLimitMaxConfiguration);
rateLimitMaxConfiguration = newRateLimitMaxConfiguration; rateLimitMaxConfiguration = newRateLimitMaxConfiguration;
} }
} catch (error) { } catch (error) {
logger.error(`Error syncing rate limit configurations: %o`, error); logger.error(error, "Error syncing rate limit configurations");
} }
}; };
@@ -238,11 +238,11 @@ export const secretScanningQueueFactory = ({
}); });
queueService.listen(QueueName.SecretPushEventScan, "failed", (job, err) => { queueService.listen(QueueName.SecretPushEventScan, "failed", (job, err) => {
logger.error("Failed to secret scan on push", job?.data, err); logger.error(err, "Failed to secret scan on push", job?.data);
}); });
queueService.listen(QueueName.SecretFullRepoScan, "failed", (job, err) => { queueService.listen(QueueName.SecretFullRepoScan, "failed", (job, err) => {
logger.error("Failed to do full repo secret scan", job?.data, err); logger.error(err, "Failed to do full repo secret scan", job?.data);
}); });
return { startFullRepoScan, startPushEventScan }; return { startFullRepoScan, startPushEventScan };
+2 -2
View File
@@ -1,7 +1,7 @@
import { Logger } from "pino";
import { z } from "zod"; import { z } from "zod";
import { removeTrailingSlash } from "../fn"; import { removeTrailingSlash } from "../fn";
import { CustomLogger } from "../logger/logger";
import { zpStr } from "../zod"; import { zpStr } from "../zod";
export const GITLAB_URL = "https://gitlab.com"; export const GITLAB_URL = "https://gitlab.com";
@@ -212,7 +212,7 @@ let envCfg: Readonly<z.infer<typeof envSchema>>;
export const getConfig = () => envCfg; export const getConfig = () => envCfg;
// cannot import singleton logger directly as it needs config to load various transport // cannot import singleton logger directly as it needs config to load various transport
export const initEnvConfig = (logger?: Logger) => { export const initEnvConfig = (logger?: CustomLogger) => {
const parsedEnv = envSchema.safeParse(process.env); const parsedEnv = envSchema.safeParse(process.env);
if (!parsedEnv.success) { if (!parsedEnv.success) {
(logger ?? console).error("Invalid environment variables. Check the error below"); (logger ?? console).error("Invalid environment variables. Check the error below");
+64 -3
View File
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
// logger follows a singleton pattern // logger follows a singleton pattern
// easier to use it that's all. // easier to use it that's all.
import { requestContext } from "@fastify/request-context";
import pino, { Logger } from "pino"; import pino, { Logger } from "pino";
import { z } from "zod"; import { z } from "zod";
@@ -13,14 +15,37 @@ const logLevelToSeverityLookup: Record<string, string> = {
"60": "CRITICAL" "60": "CRITICAL"
}; };
// eslint-disable-next-line import/no-mutable-exports
export let logger: Readonly<Logger>;
// akhilmhdh: // akhilmhdh:
// The logger is not placed in the main app config to avoid a circular dependency. // The logger is not placed in the main app config to avoid a circular dependency.
// The config requires the logger to display errors when an invalid environment is supplied. // The config requires the logger to display errors when an invalid environment is supplied.
// On the other hand, the logger needs the config to obtain credentials for AWS or other transports. // On the other hand, the logger needs the config to obtain credentials for AWS or other transports.
// By keeping the logger separate, it becomes an independent package. // By keeping the logger separate, it becomes an independent package.
// We define our own custom logger interface to enforce structure to the logging methods.
export interface CustomLogger extends Omit<Logger, "info" | "error" | "warn" | "debug"> {
info: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(obj: unknown, msg?: string, ...args: any[]): void;
};
error: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(obj: unknown, msg?: string, ...args: any[]): void;
};
warn: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(obj: unknown, msg?: string, ...args: any[]): void;
};
debug: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(obj: unknown, msg?: string, ...args: any[]): void;
};
}
// eslint-disable-next-line import/no-mutable-exports
export let logger: Readonly<CustomLogger>;
const loggerConfig = z.object({ const loggerConfig = z.object({
AWS_CLOUDWATCH_LOG_GROUP_NAME: z.string().default("infisical-log-stream"), AWS_CLOUDWATCH_LOG_GROUP_NAME: z.string().default("infisical-log-stream"),
AWS_CLOUDWATCH_LOG_REGION: z.string().default("us-east-1"), AWS_CLOUDWATCH_LOG_REGION: z.string().default("us-east-1"),
@@ -62,6 +87,17 @@ const redactedKeys = [
"config" "config"
]; ];
const UNKNOWN_REQUEST_ID = "UNKNOWN_REQUEST_ID";
const extractRequestId = () => {
try {
return requestContext.get("requestId") || UNKNOWN_REQUEST_ID;
} catch (err) {
console.log("failed to get request context", err);
return UNKNOWN_REQUEST_ID;
}
};
export const initLogger = async () => { export const initLogger = async () => {
const cfg = loggerConfig.parse(process.env); const cfg = loggerConfig.parse(process.env);
const targets: pino.TransportMultiOptions["targets"][number][] = [ const targets: pino.TransportMultiOptions["targets"][number][] = [
@@ -94,6 +130,30 @@ export const initLogger = async () => {
targets targets
}); });
const wrapLogger = (originalLogger: Logger): CustomLogger => {
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any
originalLogger.info = (obj: unknown, msg?: string, ...args: any[]) => {
return originalLogger.child({ requestId: extractRequestId() }).info(obj, msg, ...args);
};
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any
originalLogger.error = (obj: unknown, msg?: string, ...args: any[]) => {
return originalLogger.child({ requestId: extractRequestId() }).error(obj, msg, ...args);
};
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any
originalLogger.warn = (obj: unknown, msg?: string, ...args: any[]) => {
return originalLogger.child({ requestId: extractRequestId() }).warn(obj, msg, ...args);
};
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-explicit-any
originalLogger.debug = (obj: unknown, msg?: string, ...args: any[]) => {
return originalLogger.child({ requestId: extractRequestId() }).debug(obj, msg, ...args);
};
return originalLogger;
};
logger = pino( logger = pino(
{ {
mixin(_context, level) { mixin(_context, level) {
@@ -113,5 +173,6 @@ export const initLogger = async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
transport transport
); );
return logger;
return wrapLogger(logger);
}; };
+13 -2
View File
@@ -10,13 +10,15 @@ import fastifyFormBody from "@fastify/formbody";
import helmet from "@fastify/helmet"; import helmet from "@fastify/helmet";
import type { FastifyRateLimitOptions } from "@fastify/rate-limit"; import type { FastifyRateLimitOptions } from "@fastify/rate-limit";
import ratelimiter from "@fastify/rate-limit"; import ratelimiter from "@fastify/rate-limit";
import { fastifyRequestContext } from "@fastify/request-context";
import fastify from "fastify"; import fastify from "fastify";
import { Knex } from "knex"; import { Knex } from "knex";
import { Logger } from "pino";
import { HsmModule } from "@app/ee/services/hsm/hsm-types"; import { HsmModule } from "@app/ee/services/hsm/hsm-types";
import { TKeyStoreFactory } from "@app/keystore/keystore"; import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig, IS_PACKAGED } from "@app/lib/config/env"; import { getConfig, IS_PACKAGED } from "@app/lib/config/env";
import { CustomLogger } from "@app/lib/logger/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TQueueServiceFactory } from "@app/queue"; import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSmtpService } from "@app/services/smtp/smtp-service";
@@ -35,7 +37,7 @@ type TMain = {
auditLogDb?: Knex; auditLogDb?: Knex;
db: Knex; db: Knex;
smtp: TSmtpService; smtp: TSmtpService;
logger?: Logger; logger?: CustomLogger;
queue: TQueueServiceFactory; queue: TQueueServiceFactory;
keyStore: TKeyStoreFactory; keyStore: TKeyStoreFactory;
hsmModule: HsmModule; hsmModule: HsmModule;
@@ -47,7 +49,9 @@ export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, key
const server = fastify({ const server = fastify({
logger: appCfg.NODE_ENV === "test" ? false : logger, logger: appCfg.NODE_ENV === "test" ? false : logger,
genReqId: () => `req-${alphaNumericNanoId(14)}`,
trustProxy: true, trustProxy: true,
connectionTimeout: appCfg.isHsmConfigured ? 90_000 : 30_000, connectionTimeout: appCfg.isHsmConfigured ? 90_000 : 30_000,
ignoreTrailingSlash: true, ignoreTrailingSlash: true,
pluginTimeout: 40_000 pluginTimeout: 40_000
@@ -104,6 +108,13 @@ export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, key
await server.register(maintenanceMode); await server.register(maintenanceMode);
await server.register(fastifyRequestContext, {
defaultStoreValues: (request) => ({
requestId: request.id,
log: request.log.child({ requestId: request.id })
})
});
await server.register(registerRoutes, { smtp, queue, db, auditLogDb, keyStore, hsmModule }); await server.register(registerRoutes, { smtp, queue, db, auditLogDb, keyStore, hsmModule });
if (appCfg.isProductionMode) { if (appCfg.isProductionMode) {
+49 -30
View File
@@ -39,29 +39,42 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider
if (error instanceof BadRequestError) { if (error instanceof BadRequestError) {
void res void res
.status(HttpStatusCodes.BadRequest) .status(HttpStatusCodes.BadRequest)
.send({ statusCode: HttpStatusCodes.BadRequest, message: error.message, error: error.name }); .send({ requestId: req.id, statusCode: HttpStatusCodes.BadRequest, message: error.message, error: error.name });
} else if (error instanceof NotFoundError) { } else if (error instanceof NotFoundError) {
void res void res
.status(HttpStatusCodes.NotFound) .status(HttpStatusCodes.NotFound)
.send({ statusCode: HttpStatusCodes.NotFound, message: error.message, error: error.name }); .send({ requestId: req.id, statusCode: HttpStatusCodes.NotFound, message: error.message, error: error.name });
} else if (error instanceof UnauthorizedError) { } else if (error instanceof UnauthorizedError) {
void res void res.status(HttpStatusCodes.Unauthorized).send({
.status(HttpStatusCodes.Unauthorized) requestId: req.id,
.send({ statusCode: HttpStatusCodes.Unauthorized, message: error.message, error: error.name }); statusCode: HttpStatusCodes.Unauthorized,
message: error.message,
error: error.name
});
} else if (error instanceof DatabaseError || error instanceof InternalServerError) { } else if (error instanceof DatabaseError || error instanceof InternalServerError) {
void res void res.status(HttpStatusCodes.InternalServerError).send({
.status(HttpStatusCodes.InternalServerError) requestId: req.id,
.send({ statusCode: HttpStatusCodes.InternalServerError, message: "Something went wrong", error: error.name }); statusCode: HttpStatusCodes.InternalServerError,
message: "Something went wrong",
error: error.name
});
} else if (error instanceof GatewayTimeoutError) { } else if (error instanceof GatewayTimeoutError) {
void res void res.status(HttpStatusCodes.GatewayTimeout).send({
.status(HttpStatusCodes.GatewayTimeout) requestId: req.id,
.send({ statusCode: HttpStatusCodes.GatewayTimeout, message: error.message, error: error.name }); statusCode: HttpStatusCodes.GatewayTimeout,
message: error.message,
error: error.name
});
} else if (error instanceof ZodError) { } else if (error instanceof ZodError) {
void res void res.status(HttpStatusCodes.Unauthorized).send({
.status(HttpStatusCodes.Unauthorized) requestId: req.id,
.send({ statusCode: HttpStatusCodes.Unauthorized, error: "ValidationFailure", message: error.issues }); statusCode: HttpStatusCodes.Unauthorized,
error: "ValidationFailure",
message: error.issues
});
} else if (error instanceof ForbiddenError) { } else if (error instanceof ForbiddenError) {
void res.status(HttpStatusCodes.Forbidden).send({ void res.status(HttpStatusCodes.Forbidden).send({
requestId: req.id,
statusCode: HttpStatusCodes.Forbidden, statusCode: HttpStatusCodes.Forbidden,
error: "PermissionDenied", error: "PermissionDenied",
message: `You are not allowed to ${error.action} on ${error.subjectType}`, message: `You are not allowed to ${error.action} on ${error.subjectType}`,
@@ -74,48 +87,54 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider
}); });
} else if (error instanceof ForbiddenRequestError) { } else if (error instanceof ForbiddenRequestError) {
void res.status(HttpStatusCodes.Forbidden).send({ void res.status(HttpStatusCodes.Forbidden).send({
requestId: req.id,
statusCode: HttpStatusCodes.Forbidden, statusCode: HttpStatusCodes.Forbidden,
message: error.message, message: error.message,
error: error.name error: error.name
}); });
} else if (error instanceof RateLimitError) { } else if (error instanceof RateLimitError) {
void res.status(HttpStatusCodes.TooManyRequests).send({ void res.status(HttpStatusCodes.TooManyRequests).send({
requestId: req.id,
statusCode: HttpStatusCodes.TooManyRequests, statusCode: HttpStatusCodes.TooManyRequests,
message: error.message, message: error.message,
error: error.name error: error.name
}); });
} else if (error instanceof ScimRequestError) { } else if (error instanceof ScimRequestError) {
void res.status(error.status).send({ void res.status(error.status).send({
requestId: req.id,
schemas: error.schemas, schemas: error.schemas,
status: error.status, status: error.status,
detail: error.detail detail: error.detail
}); });
} else if (error instanceof OidcAuthError) { } else if (error instanceof OidcAuthError) {
void res void res.status(HttpStatusCodes.InternalServerError).send({
.status(HttpStatusCodes.InternalServerError) requestId: req.id,
.send({ statusCode: HttpStatusCodes.InternalServerError, message: error.message, error: error.name }); statusCode: HttpStatusCodes.InternalServerError,
message: error.message,
error: error.name
});
} else if (error instanceof jwt.JsonWebTokenError) { } else if (error instanceof jwt.JsonWebTokenError) {
const message = (() => { let errorMessage = error.message;
if (error.message === JWTErrors.JwtExpired) {
return "Your token has expired. Please re-authenticate.";
}
if (error.message === JWTErrors.JwtMalformed) {
return "The provided access token is malformed. Please use a valid token or generate a new one and try again.";
}
if (error.message === JWTErrors.InvalidAlgorithm) {
return "The access token is signed with an invalid algorithm. Please provide a valid token and try again.";
}
return error.message; if (error.message === JWTErrors.JwtExpired) {
})(); errorMessage = "Your token has expired. Please re-authenticate.";
} else if (error.message === JWTErrors.JwtMalformed) {
errorMessage =
"The provided access token is malformed. Please use a valid token or generate a new one and try again.";
} else if (error.message === JWTErrors.InvalidAlgorithm) {
errorMessage =
"The access token is signed with an invalid algorithm. Please provide a valid token and try again.";
}
void res.status(HttpStatusCodes.Forbidden).send({ void res.status(HttpStatusCodes.Forbidden).send({
requestId: req.id,
statusCode: HttpStatusCodes.Forbidden, statusCode: HttpStatusCodes.Forbidden,
error: "TokenError", error: "TokenError",
message message: errorMessage
}); });
} else { } else {
void res.status(HttpStatusCodes.InternalServerError).send({ void res.status(HttpStatusCodes.InternalServerError).send({
requestId: req.id,
statusCode: HttpStatusCodes.InternalServerError, statusCode: HttpStatusCodes.InternalServerError,
error: "InternalServerError", error: "InternalServerError",
message: "Something went wrong" message: "Something went wrong"
+1 -1
View File
@@ -19,7 +19,7 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) =>
app.on("installation", async (context) => { app.on("installation", async (context) => {
const { payload } = context; const { payload } = context;
logger.info("Installed secret scanner to:", { repositories: payload.repositories }); logger.info({ repositories: payload.repositories }, "Installed secret scanner to");
}); });
app.on("push", async (context) => { app.on("push", async (context) => {
@@ -30,27 +30,32 @@ export const integrationAuthPubSchema = IntegrationAuthsSchema.pick({
export const DefaultResponseErrorsSchema = { export const DefaultResponseErrorsSchema = {
400: z.object({ 400: z.object({
requestId: z.string(),
statusCode: z.literal(400), statusCode: z.literal(400),
message: z.string(), message: z.string(),
error: z.string() error: z.string()
}), }),
404: z.object({ 404: z.object({
requestId: z.string(),
statusCode: z.literal(404), statusCode: z.literal(404),
message: z.string(), message: z.string(),
error: z.string() error: z.string()
}), }),
401: z.object({ 401: z.object({
requestId: z.string(),
statusCode: z.literal(401), statusCode: z.literal(401),
message: z.any(), message: z.any(),
error: z.string() error: z.string()
}), }),
403: z.object({ 403: z.object({
requestId: z.string(),
statusCode: z.literal(403), statusCode: z.literal(403),
message: z.string(), message: z.string(),
details: z.any().optional(), details: z.any().optional(),
error: z.string() error: z.string()
}), }),
500: z.object({ 500: z.object({
requestId: z.string(),
statusCode: z.literal(500), statusCode: z.literal(500),
message: z.string(), message: z.string(),
error: z.string() error: z.string()
+10 -7
View File
@@ -285,11 +285,14 @@ export const projectQueueFactory = ({
if (!orgMembership) { if (!orgMembership) {
// This can happen. Since we don't remove project memberships and project keys when a user is removed from an org, this is a valid case. // This can happen. Since we don't remove project memberships and project keys when a user is removed from an org, this is a valid case.
logger.info("User is not in organization", { logger.info(
userId: key.receiverId, {
orgId: project.orgId, userId: key.receiverId,
projectId: project.id orgId: project.orgId,
}); projectId: project.id
},
"User is not in organization"
);
// eslint-disable-next-line no-continue // eslint-disable-next-line no-continue
continue; continue;
} }
@@ -551,10 +554,10 @@ export const projectQueueFactory = ({
.catch(() => [null]); .catch(() => [null]);
if (!project) { if (!project) {
logger.error("Failed to upgrade project, because no project was found", data); logger.error(data, "Failed to upgrade project, because no project was found");
} else { } else {
await projectDAL.setProjectUpgradeStatus(data.projectId, ProjectUpgradeStatus.Failed); await projectDAL.setProjectUpgradeStatus(data.projectId, ProjectUpgradeStatus.Failed);
logger.error("Failed to upgrade project", err, { logger.error(err, "Failed to upgrade project", {
extra: { extra: {
project, project,
jobData: data jobData: data
+2 -2
View File
@@ -142,7 +142,7 @@ export const fnTriggerWebhook = async ({
!isDisabled && picomatch.isMatch(secretPath, hookSecretPath, { strictSlashes: false }) !isDisabled && picomatch.isMatch(secretPath, hookSecretPath, { strictSlashes: false })
); );
if (!toBeTriggeredHooks.length) return; if (!toBeTriggeredHooks.length) return;
logger.info("Secret webhook job started", { environment, secretPath, projectId }); logger.info({ environment, secretPath, projectId }, "Secret webhook job started");
const project = await projectDAL.findById(projectId); const project = await projectDAL.findById(projectId);
const webhooksTriggered = await Promise.allSettled( const webhooksTriggered = await Promise.allSettled(
toBeTriggeredHooks.map((hook) => toBeTriggeredHooks.map((hook) =>
@@ -195,5 +195,5 @@ export const fnTriggerWebhook = async ({
); );
} }
}); });
logger.info("Secret webhook job ended", { environment, secretPath, projectId }); logger.info({ environment, secretPath, projectId }, "Secret webhook job ended");
}; };
+9
View File
@@ -51,17 +51,26 @@ export enum ApiErrorTypes {
export type TApiErrors = export type TApiErrors =
| { | {
requestId: string;
error: ApiErrorTypes.ValidationError; error: ApiErrorTypes.ValidationError;
message: ZodIssue[]; message: ZodIssue[];
statusCode: 401; statusCode: 401;
} }
| { | {
requestId: string;
error: ApiErrorTypes.UnauthorizedError;
message: string;
statusCode: 401;
}
| {
requestId: string;
error: ApiErrorTypes.ForbiddenError; error: ApiErrorTypes.ForbiddenError;
message: string; message: string;
details: PureAbility["rules"]; details: PureAbility["rules"];
statusCode: 403; statusCode: 403;
} }
| { | {
requestId: string;
statusCode: 400; statusCode: 400;
message: string; message: string;
error: ApiErrorTypes.BadRequestError; error: ApiErrorTypes.BadRequestError;
+13 -3
View File
@@ -32,7 +32,12 @@ export const queryClient = new QueryClient({
{ {
title: "Validation Error", title: "Validation Error",
type: "error", type: "error",
text: "Please check the input and try again.", text: (
<div>
<p>Please check the input and try again.</p>
<p className="mt-2 text-xs">Request ID: {serverResponse.requestId}</p>
</div>
),
children: ( children: (
<Modal> <Modal>
<ModalTrigger> <ModalTrigger>
@@ -72,7 +77,8 @@ export const queryClient = new QueryClient({
{ {
title: "Forbidden Access", title: "Forbidden Access",
type: "error", type: "error",
text: serverResponse.message,
text: `${serverResponse.message} [requestId=${serverResponse.requestId}]`,
children: serverResponse?.details?.length ? ( children: serverResponse?.details?.length ? (
<Modal> <Modal>
<ModalTrigger> <ModalTrigger>
@@ -165,7 +171,11 @@ export const queryClient = new QueryClient({
); );
return; return;
} }
createNotification({ title: "Bad Request", type: "error", text: serverResponse.message }); createNotification({
title: "Bad Request",
type: "error",
text: `${serverResponse.message} [requestId=${serverResponse.requestId}]`
});
} }
} }
}), }),
@@ -63,32 +63,21 @@ export const IdentityClientSecretModal = ({ popUp, handlePopUpToggle }: Props) =
}; };
const onFormSubmit = async ({ description, ttl, numUsesLimit }: FormData) => { const onFormSubmit = async ({ description, ttl, numUsesLimit }: FormData) => {
try { const { clientSecret } = await createClientSecret({
const { clientSecret } = await createClientSecret({ identityId: popUpData.identityId,
identityId: popUpData.identityId, description,
description, ttl: Number(ttl),
ttl: Number(ttl), numUsesLimit: Number(numUsesLimit)
numUsesLimit: Number(numUsesLimit) });
});
setToken(clientSecret); setToken(clientSecret);
createNotification({ createNotification({
text: "Successfully created client secret", text: "Successfully created client secret",
type: "success" type: "success"
}); });
reset(); reset();
} catch (err) {
console.error(err);
const error = err as any;
const text = error?.response?.data?.message ?? "Failed to create client secret";
createNotification({
text,
type: "error"
});
}
}; };
return ( return (