diff --git a/Dockerfile b/Dockerfile index 8102e42e..1b2243c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,5 @@ -# Use the latest LTS version of Node.js -# https://hub.docker.com/_/node -FROM node:22@sha256:37ff334612f77d8f999c10af8797727b731629c26f2e83caa6af390998bdc49c AS build-stage +# BUILD STAGE +FROM node:24-slim@sha256:36ae19f59c91f3303c7a648f07493fe14c4bd91320ac8d898416327bacf1bbfa AS build-stage WORKDIR /usr/src/app # Copy package.json and package-lock.json @@ -14,11 +13,21 @@ ENV NODE_ENV=production COPY . . RUN npm run build -# The same image but now only install the production dependencies as the frontend is already built using vite in the build-stage -FROM gcr.io/distroless/nodejs22-debian12@sha256:b765815eafacee5222bfa50179028f41dd8c642b68ad68ec4e6922d3b1ff2710 AS production +# Remove dev dependencies so the node_modules directory that we COPY into the distroless image contains only runtime dependencies +RUN npm prune --omit=dev + +# PRODUCTION STAGE +FROM gcr.io/distroless/nodejs24-debian12@sha256:20a51c926c0bb68a9b1f7059c81516da002655f8a896a2cb7bc56b56974782b3 AS production WORKDIR /usr/src/app -COPY --from=build-stage /usr/src/app /usr/src/app +# Copy built files +COPY --from=build-stage /usr/src/app/dist/client /usr/src/app/dist/client +COPY --from=build-stage /usr/src/app/dist/vite.config.json /usr/src/app/dist/vite.config.json +COPY --from=build-stage /usr/src/app/dist/server /usr/src/app/server +COPY --from=build-stage /usr/src/app/dist/server.js /usr/src/app/server.js +COPY --from=build-stage /usr/src/app/public /usr/src/app/public +COPY --from=build-stage /usr/src/app/node_modules /usr/src/app/node_modules +# Run CMD ["server.js"] diff --git a/README.md b/README.md index e60ec827..82d022a7 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,10 @@ The UI will be served on http://localhost:5173. To enable local development with Safari, follow these steps on your local machine: 1. **Update Cookie Settings:** - In [`server/encrypted-session.js`](server/encrypted-session.js), set the `secure` property to `false` in both occurrences. + In [`server/encrypted-session.js`](server/encrypted-session.ts), set the `secure` property to `false` in both occurrences. 2. **Disable Helmet Registration:** - In [`server.js`](server.js), comment out or remove the registration of `helmet`. + In [`server.js`](server.ts), comment out or remove the registration of `helmet`. ### Build & Production diff --git a/package-lock.json b/package-lock.json index f87316c1..f35bc273 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,15 +70,17 @@ "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.2.0", + "fastify-tsconfig": "^3.0.0", "globals": "^16.0.0", "prettier": "^3.5.3", + "ts-node": "^10.9.2", "typescript": "^5.7.3", "typescript-eslint": "^8.26.1", "vite": "^6.3.4", "vitest": "^3.1.4" }, "engines": { - "node": "^22.0.0", + "node": "^24.0.0", "npm": "^11.0.0" } }, @@ -453,6 +455,30 @@ "node": ">=6.9.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@cypress/request": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", @@ -4183,6 +4209,34 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -5511,6 +5565,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -5684,6 +5751,13 @@ ], "license": "MIT" }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -7029,6 +7103,13 @@ } } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-fetch": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", @@ -7458,6 +7539,16 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -8981,6 +9072,26 @@ "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==", "license": "MIT" }, + "node_modules/fastify-tsconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fastify-tsconfig/-/fastify-tsconfig-3.0.0.tgz", + "integrity": "sha512-TxFM9+MUUM2Ub6chZbP5sPNUFaPWA86kHU0VRd4o9OP6PBP92cj9c4/IEsnLoVHcLgrgXf2GUXWUzkJAO9iKFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/fastify/node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -11552,6 +11663,13 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -14767,6 +14885,50 @@ "dev": true, "license": "MIT" }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -15225,6 +15387,13 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -15802,6 +15971,16 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 579b079e..5ed46798 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,14 @@ "version": "0.0.0", "type": "module", "engines": { - "node": "^22.0.0", + "node": "^24.0.0", "npm": "^11.0.0" }, "scripts": { - "dev": "node server.js --local-dev", - "start": "node server.js", - "build": "tsc && vite build", + "dev": "node --loader ts-node/esm ./server.ts --local-dev", + "start": "node dist/server/server.js", + "build": "tsc && npm run build:server && vite build", + "build:server": "tsc -p tsconfig.server.json", "lint": "eslint ./src --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", "test:vi": "vitest", @@ -81,8 +82,10 @@ "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.2.0", + "fastify-tsconfig": "^3.0.0", "globals": "^16.0.0", "prettier": "^3.5.3", + "ts-node": "^10.9.2", "typescript": "^5.7.3", "typescript-eslint": "^8.26.1", "vite": "^6.3.4", diff --git a/server.js b/server.ts similarity index 96% rename from server.js rename to server.ts index 6debe63a..d2993a27 100644 --- a/server.js +++ b/server.ts @@ -20,6 +20,7 @@ if (!process.env.BFF_SENTRY_DSN || process.env.BFF_SENTRY_DSN.trim() === '') { beforeSend(event) { if (event.request && event.request.cookies) { event.request.cookies = Object.keys(event.request.cookies).reduce((acc, key) => { + // @ts-ignore acc[key] = ''; return acc; }, {}); @@ -49,8 +50,10 @@ Sentry.setupFastifyErrorHandler(fastify); await fastify.register(envPlugin); let sentryHost = ''; +// @ts-ignore if (fastify.config.VITE_SENTRY_DSN && fastify.config.VITE_SENTRY_DSN.length > 0) { try { + // @ts-ignore sentryHost = new URL(fastify.config.VITE_SENTRY_DSN).hostname; } catch { console.log('VITE_SENTRY_DSN is not a valid URL'); @@ -63,6 +66,7 @@ fastify.register(helmet, { directives: { 'connect-src': ["'self'", 'sdk.openui5.org', sentryHost], 'script-src': isLocalDev ? ["'self'", "'unsafe-inline'"] : ["'self'"], + // @ts-ignore 'frame-ancestors': [fastify.config.FRAME_ANCESTORS], }, }, @@ -78,6 +82,7 @@ await fastify.register(FastifyVite, { spa: true, }); +// @ts-ignore fastify.get('/', function (req, reply) { return reply.html(); }); @@ -88,6 +93,7 @@ fastify.listen( port: 5173, host: '0.0.0.0', }, + // @ts-ignore function (err, address) { if (err) { fastify.log.error(err); diff --git a/server/app.js b/server/app.ts similarity index 69% rename from server/app.js rename to server/app.ts index ced98ac0..eb0b6547 100644 --- a/server/app.js +++ b/server/app.ts @@ -1,13 +1,14 @@ -import path, { join, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; -import AutoLoad from "@fastify/autoload"; -import encryptedSession from "./encrypted-session.js"; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import AutoLoad from '@fastify/autoload'; +import encryptedSession from './encrypted-session.js'; export const options = {}; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +//@ts-ignore export default async function (fastify, opts) { fastify.register(encryptedSession, { ...opts, diff --git a/server/config/env.js b/server/config/env.ts similarity index 98% rename from server/config/env.js rename to server/config/env.ts index 67780753..7f063198 100644 --- a/server/config/env.js +++ b/server/config/env.ts @@ -38,6 +38,7 @@ const schema = { }, }; +// @ts-ignore async function envPlugin(fastify) { await fastify.register(fastifyEnv, { schema }); } diff --git a/server/encrypted-session.js b/server/encrypted-session.ts similarity index 97% rename from server/encrypted-session.js rename to server/encrypted-session.ts index 7678d6d6..4019c39f 100644 --- a/server/encrypted-session.js +++ b/server/encrypted-session.ts @@ -19,6 +19,7 @@ export const ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY = 'encryptionKey'; // name of the cookie that stores the session identifier on user side. export const SESSION_COOKIE_NAME = 'session'; +// @ts-ignore async function encryptedSession(fastify) { const { COOKIE_SECRET, SESSION_SECRET } = fastify.config; @@ -60,6 +61,7 @@ async function encryptedSession(fastify) { export default fp(encryptedSession); +// @ts-ignore function createStore(request) { let unencryptedStore = {}; // Private variable @@ -103,14 +105,20 @@ function createStore(request) { } return { + // @ts-ignore async set(key, value) { + // @ts-ignore unencryptedStore[key] = value; await save(); }, + // @ts-ignore get(key) { + // @ts-ignore return unencryptedStore[key]; }, + // @ts-ignore async delete(key) { + // @ts-ignore delete unencryptedStore[key]; await save(); }, @@ -121,10 +129,12 @@ function createStore(request) { }; } +// @ts-ignore function getUserEncryptionKeyFromUserCookie(request) { return request[ENCRYPTED_COOKIE_REQUEST_DECORATOR].get(ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY); } +// @ts-ignore function setUserEncryptionKeyIntoUserCookie(request, key) { request[ENCRYPTED_COOKIE_REQUEST_DECORATOR].set(ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY, key.toString('base64')); } @@ -140,6 +150,7 @@ function generateSecureEncryptionKey() { // If no adequate key is given, it throws an error // The key needs to be 32bytes (256bits) as type buffer. Needs to be cryptographically secure random generated e.g. with `crypto.randomBytes(32)` // it outputs cipherText (bas64 encoded string), the initialisation vector (iv) (hex string) and the authentication tag (hex string). +// @ts-ignore function encryptSymetric(plaintext, key) { if (key == undefined) { throw new Error('Key must be provided'); @@ -186,6 +197,7 @@ function encryptSymetric(plaintext, key) { // uses authenticated symetric encryption (aes-256-gcm) to decrypt the ciphertext with the key. // requires the ciphertext, the initialisation vector (iv)(hex string), the authentication tag (tag) (hex string) and the key (buffer) to be provided. //it thows an error if the decryption or tag verification fails +// @ts-ignore function decryptSymetric(cipherText, iv, tag, key) { if (key == undefined) { throw new Error('Key must be provided'); diff --git a/server/plugins/auth-utils.js b/server/plugins/auth-utils.ts similarity index 95% rename from server/plugins/auth-utils.js rename to server/plugins/auth-utils.ts index 18713e0d..76c69d01 100644 --- a/server/plugins/auth-utils.js +++ b/server/plugins/auth-utils.ts @@ -3,14 +3,17 @@ import crypto from 'node:crypto'; import * as Sentry from '@sentry/node'; export class AuthenticationError extends Error { + // @ts-ignore constructor(message) { super(message); this.name = this.constructor.name; + // @ts-ignore this.code = 'ERR_AUTHENTICATION'; Error.captureStackTrace(this, this.constructor); } } +// @ts-ignore async function getRemoteOpenIdConfiguration(issuerBaseUrl) { const url = new URL('/.well-known/openid-configuration', issuerBaseUrl).toString(); const res = await fetch(url); @@ -20,17 +23,20 @@ async function getRemoteOpenIdConfiguration(issuerBaseUrl) { return res.json(); } +// @ts-ignore function isAllowedRedirectTo(value) { if (!value) return true; const first = value.charAt(0); return first === '/' || first === '#'; } +// @ts-ignore async function authUtilsPlugin(fastify) { + // @ts-ignore fastify.decorate('discoverIssuerConfiguration', async (issuerBaseUrl) => { fastify.log.info({ issuer: issuerBaseUrl }, 'Discovering OpenId configuration.'); - const remoteConfiguration = await getRemoteOpenIdConfiguration(issuerBaseUrl); + const remoteConfiguration = await getRemoteOpenIdConfiguration(issuerBaseUrl) as any; // ToDo: proper typing const requiredConfiguration = { authorizationEndpoint: remoteConfiguration.authorization_endpoint, @@ -42,6 +48,7 @@ async function authUtilsPlugin(fastify) { return requiredConfiguration; }); + // @ts-ignore fastify.decorate('refreshAuthTokens', async (currentRefreshToken, oidcConfig, tokenEndpoint) => { fastify.log.info('Refreshing tokens.'); @@ -78,6 +85,7 @@ async function authUtilsPlugin(fastify) { }; }); + // @ts-ignore fastify.decorate('prepareOidcLoginRedirect', async (request, oidcConfig, authorizationEndpoint, stateKey) => { if (stateKey === undefined) { stateKey = 'oauthState'; @@ -121,6 +129,7 @@ async function authUtilsPlugin(fastify) { return url.toString(); }); + // @ts-ignore fastify.decorate('handleOidcCallback', async (request, oidcConfig, tokenEndpoint, stateKey) => { if (stateKey === undefined) { stateKey = 'oauthState'; @@ -157,7 +166,7 @@ async function authUtilsPlugin(fastify) { throw new AuthenticationError('Token exchange failed.'); } - const tokens = await response.json(); + const tokens = await response.json() as any; // ToDo: proper typing const result = { accessToken: tokens.access_token, @@ -169,11 +178,13 @@ async function authUtilsPlugin(fastify) { if (tokens.expires_in && typeof tokens.expires_in === 'number') { const expiresAt = Date.now() + tokens.expires_in * 1000; + // @ts-ignore result.expiresAt = expiresAt; } Sentry.addBreadcrumb({ category: 'auth', + // @ts-ignore message: 'Successfully authenticated user: ' + result.userInfo.email, level: 'info', }); @@ -183,6 +194,7 @@ async function authUtilsPlugin(fastify) { }); } +// @ts-ignore function extractUserInfoFromIdToken(request, idToken) { request.log.info('Extracting user info from ID token.'); diff --git a/server/plugins/http-proxy.js b/server/plugins/http-proxy.ts similarity index 98% rename from server/plugins/http-proxy.js rename to server/plugins/http-proxy.ts index 431add50..a763354e 100644 --- a/server/plugins/http-proxy.js +++ b/server/plugins/http-proxy.ts @@ -3,6 +3,7 @@ import httpProxy from '@fastify/http-proxy'; import { AuthenticationError } from './auth-utils.js'; import * as Sentry from '@sentry/node'; +// @ts-ignore function proxyPlugin(fastify) { const { API_BACKEND_URL } = fastify.config; const { OIDC_CLIENT_ID, OIDC_SCOPES } = fastify.config; @@ -10,6 +11,7 @@ function proxyPlugin(fastify) { fastify.register(httpProxy, { prefix: '/onboarding', upstream: API_BACKEND_URL, + // @ts-ignore preHandler: async (request, reply) => { request.log.info('Entering HTTP proxy preHandler.'); @@ -102,6 +104,7 @@ function proxyPlugin(fastify) { } }, replyOptions: { + // @ts-ignore rewriteRequestHeaders: (req, headers) => { const useCrate = req.headers['x-use-crate']; const accessToken = useCrate diff --git a/server/plugins/sensible.js b/server/plugins/sensible.ts similarity index 93% rename from server/plugins/sensible.js rename to server/plugins/sensible.ts index cac0adbf..1d7c5bc7 100644 --- a/server/plugins/sensible.js +++ b/server/plugins/sensible.ts @@ -1,6 +1,7 @@ import fp from 'fastify-plugin'; import sensible from '@fastify/sensible'; +// @ts-ignore function sensiblePlugin(fastify) { fastify.register(sensible, { errorHandler: false }); } diff --git a/server/routes/auth-mcp.js b/server/routes/auth-mcp.ts similarity index 97% rename from server/routes/auth-mcp.js rename to server/routes/auth-mcp.ts index 0b0fe699..ec65ea9d 100644 --- a/server/routes/auth-mcp.js +++ b/server/routes/auth-mcp.ts @@ -3,6 +3,7 @@ import { AuthenticationError } from '../plugins/auth-utils.js'; const stateSessionKey = 'oauthStateMCP'; +// @ts-ignore async function authPlugin(fastify) { const { OIDC_ISSUER, OIDC_CLIENT_ID_MCP, OIDC_REDIRECT_URI, OIDC_SCOPES, POST_LOGIN_REDIRECT } = fastify.config; @@ -11,6 +12,7 @@ async function authPlugin(fastify) { const mcpIssuerConfiguration = await fastify.discoverIssuerConfiguration(OIDC_ISSUER); fastify.decorate('mcpIssuerConfiguration', mcpIssuerConfiguration); + // @ts-ignore fastify.get('/auth/mcp/login', async function (req, reply) { const redirectUri = await fastify.prepareOidcLoginRedirect( req, @@ -26,6 +28,7 @@ async function authPlugin(fastify) { return reply.redirect(redirectUri); }); + // @ts-ignore fastify.get('/auth/mcp/callback', async function (req, reply) { try { const callbackResult = await fastify.handleOidcCallback( @@ -58,6 +61,7 @@ async function authPlugin(fastify) { } }); + // @ts-ignore fastify.get('/auth/mcp/me', async function (req, reply) { const accessToken = req.encryptedSession.get('mcp_accessToken'); diff --git a/server/routes/auth-onboarding.js b/server/routes/auth-onboarding.ts similarity index 97% rename from server/routes/auth-onboarding.js rename to server/routes/auth-onboarding.ts index 1fb9c94d..6a8e9df8 100644 --- a/server/routes/auth-onboarding.js +++ b/server/routes/auth-onboarding.ts @@ -3,6 +3,7 @@ import { AuthenticationError } from '../plugins/auth-utils.js'; const stateSessionKey = 'oauthStateOnboarding'; +// @ts-ignore async function authPlugin(fastify) { const { OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_REDIRECT_URI, OIDC_SCOPES, POST_LOGIN_REDIRECT } = fastify.config; @@ -10,6 +11,7 @@ async function authPlugin(fastify) { const issuerConfiguration = await fastify.discoverIssuerConfiguration(OIDC_ISSUER); fastify.decorate('issuerConfiguration', issuerConfiguration); + // @ts-ignore fastify.get('/auth/onboarding/login', async function (req, reply) { const redirectUri = await fastify.prepareOidcLoginRedirect( req, @@ -25,6 +27,7 @@ async function authPlugin(fastify) { return reply.redirect(redirectUri); }); + // @ts-ignore fastify.get('/auth/onboarding/callback', async function (req, reply) { try { const callbackResult = await fastify.handleOidcCallback( @@ -58,6 +61,7 @@ async function authPlugin(fastify) { } }); + // @ts-ignore fastify.get('/auth/onboarding/me', async function (req, reply) { const accessToken = req.encryptedSession.get('onboarding_accessToken'); const userInfo = req.encryptedSession.get('onboarding_userInfo'); @@ -67,6 +71,7 @@ async function authPlugin(fastify) { return reply.send({ isAuthenticated, user }); }); + // @ts-ignore fastify.post('/auth/logout', async function (req, reply) { // TODO: Idp sign out flow await req.encryptedSession.clear(); diff --git a/server/routes/feedback.js b/server/routes/feedback.ts similarity index 96% rename from server/routes/feedback.js rename to server/routes/feedback.ts index 0fccc1e2..0ae965aa 100644 --- a/server/routes/feedback.js +++ b/server/routes/feedback.ts @@ -1,9 +1,12 @@ +// @ts-ignore import fetch from 'node-fetch'; import fp from 'fastify-plugin'; +// @ts-ignore async function feedbackRoute(fastify) { const { FEEDBACK_SLACK_URL } = fastify.config; + // @ts-ignore fastify.post('/feedback', async (request, reply) => { const { message, rating, user, environment } = request.body; diff --git a/server/routes/root.js b/server/routes/root.ts similarity index 88% rename from server/routes/root.js rename to server/routes/root.ts index 5ad7e594..6f4899d1 100644 --- a/server/routes/root.js +++ b/server/routes/root.ts @@ -1,6 +1,8 @@ import fp from 'fastify-plugin'; +// @ts-ignore function rootRoutes(fastify) { + // @ts-ignore fastify.get('/', async (_request, reply) => { return reply.code(200).send({ status: 'ok', timestamp: new Date().toISOString() }); }); diff --git a/tsconfig.json b/tsconfig.json index b022f0df..bc4bdc87 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,7 @@ "noFallthroughCasesInSwitch": true, "types": ["node", "cypress"] }, - "include": ["src", "cypress.d.ts", "server.js", "i18n.ts", "server/**/*"], + "include": ["src", "cypress.d.ts", "i18n.ts"], "references": [ { "path": "./tsconfig.node.json" diff --git a/tsconfig.server.json b/tsconfig.server.json new file mode 100644 index 00000000..49567c86 --- /dev/null +++ b/tsconfig.server.json @@ -0,0 +1,7 @@ +{ + "extends": "fastify-tsconfig", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["server/**/*.ts", "server.ts"] +}