Merged in feature/redis (pull request #1478)

Distributed cache

* cache deleteKey now uses an options object instead of a lonely argument variable fuzzy

* merge

* remove debug logs and cleanup

* cleanup

* add fault handling

* add fault handling

* add pid when logging redis client creation

* add identifier when logging redis client creation

* cleanup

* feat: add redis-api as it's own app

* feature: use http wrapper for redis

* feat: add the possibility to fallback to unstable_cache

* Add error handling if redis cache is unresponsive

* add logging for unstable_cache

* merge

* don't cache errors

* fix: metadatabase on branchdeploys

* Handle when /en/destinations throws
add ErrorBoundary

* Add sentry-logging when ErrorBoundary catches exception

* Fix error handling for distributed cache

* cleanup code

* Added Application Insights back

* Update generateApiKeys script and remove duplicate

* Merge branch 'feature/redis' of bitbucket.org:scandic-swap/web into feature/redis

* merge


Approved-by: Linus Flood
This commit is contained in:
Joakim Jäderberg
2025-03-14 07:54:21 +00:00
committed by Linus Flood
parent a8304e543e
commit fa63b20ed0
141 changed files with 4404 additions and 1941 deletions

View File

@@ -0,0 +1,34 @@
import pino from "pino";
import { mask } from "./mask";
import { env } from "@/env";
const serializers: { [key: string]: pino.SerializerFn } = {
password: (payload) => {
if (payload) {
return env.IS_DEV
? mask(payload)
: mask(payload, {
visibleStart: 0,
visibleEnd: 0,
});
}
return payload;
},
email: (payload) => {
if (payload) {
return env.IS_DEV ? payload : mask(payload);
}
return payload;
},
};
export const baseLogger = pino({
level: process.env.LOG_LEVEL || "info",
timestamp: pino.stdTimeFunctions.isoTime,
serializers,
});
export const loggerModule = (loggerName: string) => {
return baseLogger.child({ module: loggerName });
};

View File

@@ -0,0 +1,42 @@
import { describe, it, expect } from "bun:test";
import { mask } from "./mask";
describe("mask", () => {
it("should return empty string for empty input", () => {
expect(mask("")).toBe("");
});
it("should mask string with default parameters", () => {
expect(mask("1234567890")).toBe("12******90");
});
it("should show custom number of characters at start", () => {
expect(mask("1234567890", { visibleStart: 3 })).toBe("123*****90");
});
it("should show custom number of characters at end", () => {
expect(mask("1234567890", { visibleStart: 2, visibleEnd: 3 })).toBe(
"12*****890",
);
});
it("should mask entire string when visible parts exceed length", () => {
expect(mask("123", { visibleStart: 2, visibleEnd: 2 })).toBe("***");
});
it("should handle undefined end part", () => {
expect(mask("1234567890", { visibleStart: 2, visibleEnd: 0 })).toBe(
"12********",
);
});
it("should handle long strings", () => {
expect(mask("12345678901234567890")).toBe("12**********90");
});
it("should handle emails", () => {
expect(mask("test.testsson@scandichotels.com")).toBe(
"te*********on@sc*********ls.com",
);
});
});

View File

@@ -0,0 +1,42 @@
/**
* Masks a string by replacing characters with a mask character
* @param value - The string to mask
* @param visibleStart - Number of characters to show at start (default: 0)
* @param visibleEnd - Number of characters to show at end (default: 4)
* @param maskChar - Character to use for masking (default: '*')
* @returns The masked string
*/
const maskChar = "*";
export function mask(
value: string,
options?: { visibleStart?: number; visibleEnd?: number; maxLength?: number },
): string {
if (!value) return "";
const { visibleStart = 2, visibleEnd = 2, maxLength = 10 } = options ?? {};
if (isEmail(value)) {
return maskEmail(value);
}
const totalVisible = visibleStart + visibleEnd;
if (value.length <= totalVisible) {
return maskChar.repeat(value.length);
}
const start = value.slice(0, visibleStart);
const middle = value.slice(visibleStart, -visibleEnd || undefined);
const end = visibleEnd ? value.slice(-visibleEnd) : "";
const maskedLength = Math.min(middle.length, maxLength);
return start + maskChar.repeat(maskedLength) + end;
}
function maskEmail(email: string): string {
const [local, domain] = email.split("@");
if (!domain || !local) return mask(email);
const [subDomain, tld] = domain.split(/\.(?=[^.]+$)/);
return `${mask(local)}@${mask(subDomain ?? "")}.${tld}`;
}
const isEmail = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);