Merged in chore/eslint9 (pull request #2029)
chore: Update to ESLint 9 * wip: apply codemod and upgrade swc plugin * Update eslint to 9 in scandic-web apply code mod to config fix existing lint issues * Remove uneccessary fixupConfigRules * Update eslint to 9 in design-system * Add lint turbo dependency * Move redis-api to eslint and prettier instead of biome * Simplify eslint configs * Clean up * Apply linting Approved-by: Linus Flood
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": ["node_modules"],
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab",
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true,
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"performance": {
|
||||
"noBarrelFile": "error",
|
||||
},
|
||||
"style": {
|
||||
"useImportType": "error",
|
||||
"useExportType": "error",
|
||||
},
|
||||
},
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"trailingCommas": "all",
|
||||
},
|
||||
},
|
||||
}
|
||||
41
apps/redis-api/eslint.config.mjs
Normal file
41
apps/redis-api/eslint.config.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import js from "@eslint/js";
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import { defineConfig } from "eslint/config";
|
||||
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
||||
|
||||
const compat = new FlatCompat({
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
extends: compat.extends("plugin:@typescript-eslint/recommended"),
|
||||
plugins: {
|
||||
"simple-import-sort": simpleImportSort,
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
},
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "redis-api",
|
||||
"name": "@scandic-hotels/redis-api",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/index.ts | pino-pretty -o '{if module}[{module}] {end}{msg}' -i pid,hostname"
|
||||
"dev": "bun --watch src/index.ts | pino-pretty -o '{if module}[{module}] {end}{msg}' -i pid,hostname",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 && tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elysiajs/server-timing": "^1.3.0",
|
||||
@@ -17,9 +18,18 @@
|
||||
"pino": "^9.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@types/bun": "latest",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"eslint": "^9",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"prettier": {
|
||||
"tabWidth": 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import "@/server/sentry.server.config";
|
||||
|
||||
import serverTiming from "@elysiajs/server-timing";
|
||||
import { swagger } from "@elysiajs/swagger";
|
||||
import * as Sentry from "@sentry/bun";
|
||||
import { Elysia } from "elysia";
|
||||
|
||||
import { swagger } from "@elysiajs/swagger";
|
||||
import { apiRoutes } from "@/routes/api";
|
||||
import { healthRoutes } from "@/routes/health";
|
||||
import { baseLogger } from "@/utils/logger";
|
||||
import { env } from "@/env";
|
||||
import serverTiming from "@elysiajs/server-timing";
|
||||
import { AuthenticationError } from "@/errors/AuthenticationError";
|
||||
import { ModelValidationError } from "@/errors/ModelValidationError";
|
||||
|
||||
import { apiRoutes } from "@/routes/api";
|
||||
import { healthRoutes } from "@/routes/health";
|
||||
import { setupShutdown } from "@/shutdown";
|
||||
import { baseLogger } from "@/utils/logger";
|
||||
|
||||
setupShutdown();
|
||||
|
||||
@@ -48,7 +48,7 @@ if (env.IS_DEV) {
|
||||
version: "1.0.0",
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AuthenticationError } from "@/errors/AuthenticationError";
|
||||
import type { Context } from "elysia";
|
||||
|
||||
import { env } from "@/env";
|
||||
import { AuthenticationError } from "@/errors/AuthenticationError";
|
||||
|
||||
const API_KEY_HEADER = "x-api-key";
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as Sentry from "@sentry/bun";
|
||||
|
||||
import { Elysia, t } from "elysia";
|
||||
import { redis } from "@/services/redis";
|
||||
|
||||
import { ModelValidationError } from "@/errors/ModelValidationError";
|
||||
import { redis } from "@/services/redis";
|
||||
import { loggerModule } from "@/utils/logger";
|
||||
import { truncate } from "@/utils/truncate";
|
||||
import { timeout } from "@/utils/timeout";
|
||||
import { truncate } from "@/utils/truncate";
|
||||
|
||||
const MIN_LENGTH = 1;
|
||||
|
||||
@@ -42,7 +42,7 @@ export const cacheRoutes = new Elysia({ prefix: "/cache" })
|
||||
{
|
||||
query: QUERY_TYPE,
|
||||
response: { 200: t.Object({ data: t.Any() }), 404: t.String() },
|
||||
}
|
||||
},
|
||||
)
|
||||
.put(
|
||||
"/",
|
||||
@@ -52,7 +52,7 @@ export const cacheRoutes = new Elysia({ prefix: "/cache" })
|
||||
|
||||
if (!body.ttl || body.ttl < 0) {
|
||||
cacheRouteLogger.warn(
|
||||
`PUT /cache ${key} with ttl=${body.ttl}, will not cache the data`
|
||||
`PUT /cache ${key} with ttl=${body.ttl}, will not cache the data`,
|
||||
);
|
||||
return status("Bad Request", "ttl is required");
|
||||
}
|
||||
@@ -66,14 +66,14 @@ export const cacheRoutes = new Elysia({ prefix: "/cache" })
|
||||
body: t.Object({ data: t.Any(), ttl: t.Number() }),
|
||||
query: QUERY_TYPE,
|
||||
response: { 204: t.Undefined(), 400: t.String() },
|
||||
}
|
||||
},
|
||||
)
|
||||
.delete(
|
||||
"/",
|
||||
async ({ query: { key, fuzzy } }) => {
|
||||
key = validateKey(key);
|
||||
cacheRouteLogger.debug(
|
||||
`DELETE /cache ${key} ${fuzzy ? "fuzzy" : ""}`
|
||||
`DELETE /cache ${key} ${fuzzy ? "fuzzy" : ""}`,
|
||||
);
|
||||
const deletedKeys: number = fuzzy
|
||||
? await deleteWithPattern(`*${key}*`)
|
||||
@@ -91,7 +91,7 @@ export const cacheRoutes = new Elysia({ prefix: "/cache" })
|
||||
200: t.Object({ deletedKeys: t.Number() }),
|
||||
400: t.String(),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function validateKey(key: string) {
|
||||
@@ -99,7 +99,7 @@ function validateKey(key: string) {
|
||||
|
||||
if (parsedKey.length < MIN_LENGTH) {
|
||||
throw new ModelValidationError(
|
||||
"Key has to be at least 1 character long"
|
||||
"Key has to be at least 1 character long",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ async function deleteWithPattern(pattern: string) {
|
||||
"MATCH",
|
||||
pattern,
|
||||
"COUNT",
|
||||
SCAN_SIZE
|
||||
SCAN_SIZE,
|
||||
);
|
||||
|
||||
cursor = newCursor;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Elysia } from "elysia";
|
||||
import { cacheRoutes } from "./cache";
|
||||
|
||||
import { apiKeyMiddleware } from "@/middleware/apiKeyMiddleware";
|
||||
|
||||
import { cacheRoutes } from "./cache";
|
||||
|
||||
export const apiRoutes = new Elysia({ prefix: "/api" })
|
||||
.guard({ beforeHandle: apiKeyMiddleware })
|
||||
.use(cacheRoutes);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
|
||||
import { redis } from "@/services/redis";
|
||||
import { baseLogger, loggerModule } from "@/utils/logger";
|
||||
import { env } from "@/env";
|
||||
import { redis } from "@/services/redis";
|
||||
import { baseLogger } from "@/utils/logger";
|
||||
|
||||
const healthLogger = baseLogger.child({
|
||||
module: "health",
|
||||
@@ -52,5 +52,5 @@ export const healthRoutes = new Elysia().get(
|
||||
duration: t.String(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import "@sentry/tracing";
|
||||
import { env } from "@/env";
|
||||
|
||||
import * as Sentry from "@sentry/bun";
|
||||
|
||||
import { env } from "@/env";
|
||||
|
||||
Sentry.init({
|
||||
dsn: env.SENTRY_DSN,
|
||||
enabled: env.SENTRY_ENABLED,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { redisConfig, env } from "@/env";
|
||||
import ioredis from "ioredis";
|
||||
|
||||
import { env, redisConfig } from "@/env";
|
||||
|
||||
const redis = new ioredis({
|
||||
host: redisConfig.host,
|
||||
port: redisConfig.port,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { loggerModule } from "@/utils/logger";
|
||||
import { redis } from "@/services/redis";
|
||||
import { loggerModule } from "@/utils/logger";
|
||||
|
||||
const shutdownLogger = loggerModule("shutdown");
|
||||
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
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,
|
||||
});
|
||||
}
|
||||
import { mask } from "./mask";
|
||||
|
||||
return payload;
|
||||
},
|
||||
email: (payload) => {
|
||||
if (payload) {
|
||||
return env.IS_DEV ? payload : mask(payload);
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
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,
|
||||
level: process.env.LOG_LEVEL || "info",
|
||||
timestamp: pino.stdTimeFunctions.isoTime,
|
||||
serializers,
|
||||
});
|
||||
|
||||
export const loggerModule = (loggerName: string) => {
|
||||
return baseLogger.child({ module: loggerName });
|
||||
return baseLogger.child({ module: loggerName });
|
||||
};
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { describe, expect, it } from "bun:test";
|
||||
|
||||
import { mask } from "./mask";
|
||||
|
||||
describe("mask", () => {
|
||||
it("should return empty string for empty input", () => {
|
||||
expect(mask("")).toBe("");
|
||||
});
|
||||
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 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 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 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 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 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 long strings", () => {
|
||||
expect(mask("12345678901234567890")).toBe("12**********90");
|
||||
});
|
||||
|
||||
it("should handle emails", () => {
|
||||
expect(mask("test.testsson@scandichotels.com")).toBe(
|
||||
"te*********on@sc*********ls.com",
|
||||
);
|
||||
});
|
||||
it("should handle emails", () => {
|
||||
expect(mask("test.testsson@scandichotels.com")).toBe(
|
||||
"te*********on@sc*********ls.com",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user