From 2aeb9d66039d9185eee71f4587331b88db27b359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Skr=C3=B8vseth?= Date: Mon, 26 Aug 2024 11:33:31 +0200 Subject: [PATCH] WIP --- README.md | 9 +- server/biome.json | 6 ++ server/bun.lockb | Bin 42849 -> 73653 bytes server/bunfig.toml | 2 - server/package.json | 1 + server/src/index-file.ts | 16 ++-- server/src/logger.ts | 11 +++ server/src/plugins/api-proxy.ts | 4 +- server/src/plugins/error-report.ts | 25 ++++++ .../src/plugins/frontend-log/frontend-log.ts | 81 +++++++++++++++++ server/src/plugins/frontend-log/types.ts | 83 ++++++++++++++++++ server/src/plugins/http-logger.ts | 2 +- server/src/plugins/serve-index/serve-index.ts | 3 +- server/src/routes/error-report.ts | 15 ---- server/src/routes/frontend-log.ts | 41 --------- 15 files changed, 227 insertions(+), 72 deletions(-) delete mode 100644 server/bunfig.toml create mode 100644 server/src/plugins/error-report.ts create mode 100644 server/src/plugins/frontend-log/frontend-log.ts create mode 100644 server/src/plugins/frontend-log/types.ts delete mode 100644 server/src/routes/error-report.ts delete mode 100644 server/src/routes/frontend-log.ts diff --git a/README.md b/README.md index feae1897..278266f2 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,13 @@ Digital innsending av klager og anker. Samt ettersendelse. # Komme i gang ## Autorisering mot @navikt-NPM-registeret -1. Lag et Personal Access Token (PAT) med scope: `read:packages`. _PAT-en må være autorisert for organisasjonen `navikt`._ -2. Sett verdien i miljøvariabelen NODE_AUTH_TOKEN +1. [Lag et Personal Access Token (PAT)](https://github.com/settings/tokens) med scope: `read:packages`. _Tokenet må være autorisert for organisasjonen `navikt`._ +2. Opprett filen `bunfig.toml` i din `$HOME`-mappe med følgende innhold: + ```toml + [install.scopes] + "@navikt" = { token = "ghp_Qj6Xxn8HTUSJL9dNiZ0TW7R5YvupTZclTXsK", url = "https://npm.pkg.github.com/" } + ``` +3. Bytt ut `ghp_Qj6Xxn8HTUSJL9dNiZ0TW7R5YvupTZclTXsK` med ditt eget token. ### Referanser - https://github.com/navikt/nav-dekoratoren-moduler?tab=readme-ov-file#kom-i-gang diff --git a/server/biome.json b/server/biome.json index 29a84b57..89c971ec 100644 --- a/server/biome.json +++ b/server/biome.json @@ -30,6 +30,12 @@ "noUnusedImports": { "level": "error" } + }, + "style": { + "noUnusedTemplateLiteral": { + "level": "warn", + "fix": "unsafe" + } } } }, diff --git a/server/bun.lockb b/server/bun.lockb index 6c2d08901a7b531744beef4a90ac930689dfbd0e..b2353f9cfb72434c645f93547d0e4662f4c45b1d 100755 GIT binary patch delta 24716 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
@*rzpvq{=sBuaZXVt^k72y$;;3t}f*BRJ9LHG( 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 K+in(DeE$xXK!6(n diff --git a/server/bunfig.toml b/server/bunfig.toml deleted file mode 100644 index 4dfda52d..00000000 --- a/server/bunfig.toml +++ /dev/null @@ -1,2 +0,0 @@ -[install.scopes] -"@navikt" = { token = "$NODE_AUTH_TOKEN", url = "https://npm.pkg.github.com/" } diff --git a/server/package.json b/server/package.json index 312485db..5d799dde 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,7 @@ "@fastify/cors": "9.0.1", "@fastify/http-proxy": "9.5.0", "@fastify/type-provider-typebox": "4.1.0", + "@navikt/nav-dekoratoren-moduler": "^1.6.9", "fastify": "4.28.1", "fastify-metrics": "11.0.0", "jose": "5.7.0", diff --git a/server/src/index-file.ts b/server/src/index-file.ts index fecab47a..4e319c17 100644 --- a/server/src/index-file.ts +++ b/server/src/index-file.ts @@ -43,15 +43,13 @@ class IndexFile { const indexHtml = await injectDecoratorServerSide({ env: isDeployedToProd ? 'prod' : 'dev', filePath: this.INDEX_HTML_PATH, - params: { - simple: true, - chatbot: true, - redirectToApp: true, - logoutUrl: '/oauth2/logout', - context: 'privatperson', - level: 'Level4', - logoutWarning: true, - }, + simple: true, + chatbot: true, + redirectToApp: true, + logoutUrl: '/oauth2/logout', + context: 'privatperson', + level: 'Level4', + utloggingsvarsel: true, }); const end = performance.now(); diff --git a/server/src/logger.ts b/server/src/logger.ts index 078b1fb6..f8ebb671 100644 --- a/server/src/logger.ts +++ b/server/src/logger.ts @@ -18,6 +18,17 @@ type SerializableValue = | AnyObject | AnyObject[]; +export const isSerializable = (value: unknown): value is SerializableValue => { + return ( + typeof value === 'number' || + typeof value === 'string' || + typeof value === 'boolean' || + Array.isArray(value) || + value === null || + typeof value === 'object' + ); +}; + export interface AnyObject { [key: string]: SerializableValue; } diff --git a/server/src/plugins/api-proxy.ts b/server/src/plugins/api-proxy.ts index d048c7c3..18a4e5ec 100644 --- a/server/src/plugins/api-proxy.ts +++ b/server/src/plugins/api-proxy.ts @@ -19,6 +19,8 @@ export interface ApiProxyPluginOptions { appNames: string[]; } +export const API_PROXY_PLUGIN_ID = 'api-proxy'; + export const apiProxyPlugin = fastifyPlugin( (app, { appNames }, pluginDone) => { app.decorateRequest('proxyStartTime', 0); @@ -114,7 +116,7 @@ export const apiProxyPlugin = fastifyPlugin( pluginDone(); }, - { fastify: '4', name: 'api-proxy', dependencies: [OBO_ACCESS_TOKEN_PLUGIN_ID, SERVER_TIMING_PLUGIN_ID] }, + { fastify: '4', name: API_PROXY_PLUGIN_ID, dependencies: [OBO_ACCESS_TOKEN_PLUGIN_ID, SERVER_TIMING_PLUGIN_ID] }, ); const prefixServerTimingEntry = (entry: string, appName: string): string => { diff --git a/server/src/plugins/error-report.ts b/server/src/plugins/error-report.ts new file mode 100644 index 00000000..1987af23 --- /dev/null +++ b/server/src/plugins/error-report.ts @@ -0,0 +1,25 @@ +import { getLogger, isSerializable } from '@app/logger'; +import fastifyPlugin from 'fastify-plugin'; + +const log = getLogger('frontend-error-reporter'); + +export const ERROR_REPORT_PLUGIN_ID = 'error-report'; + +export const frontendLogPlugin = fastifyPlugin( + (app, _, pluginDone) => { + app.post('/error-report', (req, reply) => { + if (!isSerializable(req.body)) { + reply.status(400).send('Invalid request body'); + + return; + } + + log.warn({ msg: 'Error report', data: req.body }); + + reply.status(200).send(); + }); + + pluginDone(); + }, + { fastify: '4', name: ERROR_REPORT_PLUGIN_ID }, +); diff --git a/server/src/plugins/frontend-log/frontend-log.ts b/server/src/plugins/frontend-log/frontend-log.ts new file mode 100644 index 00000000..709bc330 --- /dev/null +++ b/server/src/plugins/frontend-log/frontend-log.ts @@ -0,0 +1,81 @@ +import { getLogger } from '@app/logger'; +import { FrontendEventTypes, Level } from '@app/plugins/frontend-log/types'; +import { Type, type TypeBoxTypeProvider } from '@fastify/type-provider-typebox'; +import fastifyPlugin from 'fastify-plugin'; + +const log = getLogger('frontend-log'); + +export const FRONTEND_LOG_PLUGIN_ID = 'frontend-log'; + +export const frontendLogPlugin = fastifyPlugin( + (app, _, pluginDone) => { + app.withTypeProvider().post( + '/frontend-log', + { + schema: { + body: Type.Composite([ + Type.Object({ + session_id: Type.String(), + level: Type.Enum(Level), + client_timestamp: Type.Number({ description: 'Current client Unix timestamp in milliseconds.' }), + token_expires: Type.Optional(Type.Number({ description: 'Milliseconds until the token expires.' })), + is_logged_in: Type.Boolean(), + client_version: Type.String(), + session_time: Type.Number({ description: 'Milliseconds since start of session.' }), + session_time_formatted: Type.String({ + description: 'Formatted time since start of session.', + format: 'time', + examples: ['1:35:45.987'], + }), + route: Type.String({ + description: 'Current path.', + examples: ['/nb/sak/uuid-uuid-uuid-uuid/begrunnelse'], + }), + message: Type.String(), + }), + Type.Union([ + // Navigation event + Type.Object({ + type: Type.Literal(FrontendEventTypes.NAVIGATION), + }), + // App event + Type.Object({ + type: Type.Literal(FrontendEventTypes.APP), + action: Type.String(), + }), + // Error event + Type.Object( + { + type: Type.Literal(FrontendEventTypes.ERROR), + message: Type.String(), + stack: Type.Optional(Type.String({ description: 'Stack trace of the error.' })), + }, + { description: 'Error event.' }, + ), + // API event + Type.Object( + { + type: Type.Literal(FrontendEventTypes.API), + request: Type.String(), + response_time: Type.Number({ description: 'API response time in milliseconds.' }), + status: Type.Union([Type.Number(), Type.String()]), + }, + { description: 'API event.' }, + ), + ]), + ]), + }, + }, + (req, reply) => { + const { level, message, ...data } = req.body; + + log[level]({ msg: message, data }); + + reply.status(200).send(); + }, + ); + + pluginDone(); + }, + { fastify: '4', name: FRONTEND_LOG_PLUGIN_ID }, +); diff --git a/server/src/plugins/frontend-log/types.ts b/server/src/plugins/frontend-log/types.ts new file mode 100644 index 00000000..2ea02ea0 --- /dev/null +++ b/server/src/plugins/frontend-log/types.ts @@ -0,0 +1,83 @@ +export enum Level { + DEBUG = 'debug', + INFO = 'info', + WARN = 'warn', + ERROR = 'error', +} + +export enum FrontendEventTypes { + NAVIGATION = 'navigation', + APP = 'app', + ERROR = 'error', + API = 'api', + SESSION = 'session', +} + +interface BaseEventData { + session_id: string; + level: Level; + /** Current client Unix timestamp in milliseconds. */ + client_timestamp: number; + /** Milliseconds until the token expires. */ + token_expires?: number; + /** If the user is logged in. */ + is_logged_in: boolean; + /** Current client version. */ + client_version: string; + /** Milliseconds since start of session. */ + session_time: number; + /** Formatted time since start of session. + * @example `1:35:45.987` + */ + session_time_formatted: string; + /** Current path. + * @example `/nb/klage/1234/begrunnelse` + * */ + route: string; + message: string; +} + +interface NavigationEvent extends BaseEventData { + type: FrontendEventTypes.NAVIGATION; +} + +interface AppEvent extends BaseEventData { + type: FrontendEventTypes.APP; + action: string; +} + +interface ErrorEvent extends BaseEventData { + type: FrontendEventTypes.ERROR; + message: string; + stack?: string; +} + +interface ApiEvent extends BaseEventData { + type: FrontendEventTypes.API; + request: string; + response_time: number; + status: number | string; +} + +enum SessionAction { + /** Load session case */ + LOAD = 'load', + /** Create session case */ + CREATE = 'create', + /** Load or create session case */ + LOAD_OR_CREATE = 'load-create', + /** Delete session case */ + DELETE = 'delete', + /** Set session case */ + SET = 'set', + /** Update session case */ + UPDATE = 'update', +} + +interface SessionEvent extends BaseEventData { + type: FrontendEventTypes.SESSION; + message: string; + action: SessionAction; +} + +export type FrontendLogEvent = NavigationEvent | AppEvent | ErrorEvent | ApiEvent | SessionEvent; diff --git a/server/src/plugins/http-logger.ts b/server/src/plugins/http-logger.ts index afd49f91..feaa79f9 100644 --- a/server/src/plugins/http-logger.ts +++ b/server/src/plugins/http-logger.ts @@ -2,7 +2,7 @@ import { getDuration } from '@app/helpers/duration'; import { type AnyObject, getLogger } from '@app/logger'; import { PROXY_VERSION_PLUGIN_ID } from '@app/plugins/proxy-version'; import { SERVE_ASSETS_PLUGIN_ID } from '@app/plugins/serve-assets'; -import { SERVE_INDEX_PLUGIN_ID } from '@app/plugins/serve-index'; +import { SERVE_INDEX_PLUGIN_ID } from '@app/plugins/serve-index/serve-index'; import fastifyPlugin from 'fastify-plugin'; export const HTTP_LOGGER_PLUGIN_ID = 'http-logger'; diff --git a/server/src/plugins/serve-index/serve-index.ts b/server/src/plugins/serve-index/serve-index.ts index 3069c644..97ab00ba 100644 --- a/server/src/plugins/serve-index/serve-index.ts +++ b/server/src/plugins/serve-index/serve-index.ts @@ -1,6 +1,7 @@ import { isDeployedToProd } from '@app/config/env'; import { indexFile } from '@app/index-file'; import { getLogger } from '@app/logger'; +import { API_PROXY_PLUGIN_ID } from '@app/plugins/api-proxy'; import { externalRedirectCounter, viewCountCounter } from '@app/plugins/serve-index/counters'; import { getPaths } from '@app/plugins/serve-index/get-paths'; import { removeSaksnummer } from '@app/plugins/serve-index/remove-saksnummer'; @@ -43,7 +44,7 @@ export const serveIndexPlugin = fastifyPlugin( pluginDone(); }, - { fastify: '4', name: SERVE_INDEX_PLUGIN_ID }, + { fastify: '4', name: SERVE_INDEX_PLUGIN_ID, dependencies: [API_PROXY_PLUGIN_ID] }, ); const YTELSE_OVERVIEW_URL = isDeployedToProd ? 'https://www.nav.no/klage' : 'https://www.ekstern.dev.nav.no/klage'; diff --git a/server/src/routes/error-report.ts b/server/src/routes/error-report.ts deleted file mode 100644 index a4feab3b..00000000 --- a/server/src/routes/error-report.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { type SerializableObject, getLogger } from '@app/logger/logger'; -import { type Request, Router, json } from 'express'; - -const router = Router(); - -const log = getLogger('frontend-error-reporter'); - -export const errorReporter = () => { - router.post('/error-report', json(), (req: Request, res) => { - log.warn({ message: 'Error report', data: req.body }); - res.status(200).send(); - }); - - return router; -}; diff --git a/server/src/routes/frontend-log.ts b/server/src/routes/frontend-log.ts deleted file mode 100644 index ba5a6a88..00000000 --- a/server/src/routes/frontend-log.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Level, type SerializableObject, getLogger } from '@app/logger/logger'; -import { FrontendEventTypes, type FrontendLogEvent } from '@app/logger/types'; -import { type Request, Router, json } from 'express'; - -const router = Router(); - -const log = getLogger('frontend-log'); - -export const frontendLog = () => { - router.post('/frontend-log', json(), (req: Request, res) => { - if (!isFrontendLog(req.body)) { - return res.status(400).send(); - } - - const { level, message, ...data } = req.body; - - log[level]({ message, data }); - - res.status(200).send(); - }); - - return router; -}; - -const LOG_LEVELS = Object.values(Level); -const EVENT_TYPES = Object.values(FrontendEventTypes); - -const isFrontendLog = (data: unknown): data is FrontendLogEvent => { - if (typeof data !== 'object' || data === null) { - return false; - } - - return ( - 'level' in data && - typeof data.level === 'string' && - LOG_LEVELS.some((level) => level === data.level) && - 'type' in data && - typeof data.type === 'string' && - EVENT_TYPES.some((type) => type === data.type) - ); -};