Skip to content

Commit

Permalink
Update server dependencies inc. Fastify v5
Browse files Browse the repository at this point in the history
  • Loading branch information
cskrov committed Nov 1, 2024
1 parent 9b17e05 commit df6e9b1
Show file tree
Hide file tree
Showing 19 changed files with 109 additions and 94 deletions.
Binary file modified server/bun.lockb
Binary file not shown.
20 changes: 10 additions & 10 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
"lint": "biome check"
},
"dependencies": {
"@fastify/cors": "9.0.1",
"@fastify/http-proxy": "9.5.0",
"@fastify/type-provider-typebox": "4.1.0",
"fastify": "4.28.1",
"fastify-metrics": "11.0.0",
"happy-dom": "15.6.0",
"@fastify/cors": "10.0.1",
"@fastify/http-proxy": "10.0.1",
"@fastify/type-provider-typebox": "5.0.0",
"fastify": "5.1.0",
"fastify-metrics": "12.1.0",
"happy-dom": "15.7.4",
"jose": "5.8.0",
"openid-client": "5.6.5",
"prom-client": "15.1.3",
"redis": "4.7.0"
},
"devDependencies": {
"@biomejs/biome": "1.8.3",
"@types/bun": "1.1.8",
"@types/node": "22.5.1",
"typescript": "5.5.4"
"@biomejs/biome": "1.9.4",
"@types/bun": "1.1.12",
"@types/node": "22.8.6",
"typescript": "5.6.3"
}
}
19 changes: 18 additions & 1 deletion server/src/auth/cache/cache.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createHash } from 'node:crypto';
import { OboMemoryCache } from '@app/auth/cache/memory-cache';
import { OboRedisCache } from '@app/auth/cache/redis-cache';
import { optionalEnvString } from '@app/config/env-var';
Expand Down Expand Up @@ -47,6 +48,16 @@ class OboTieredCache {
return null;
}

public getCached(key: string): string | null {
if (this.#oboMemoryCache === null) {
return null;
}

const memoryHit = this.#oboMemoryCache.get(key);

return memoryHit?.token ?? null;
}

public async set(key: string, token: string, expiresAt: number): Promise<void> {
this.#oboMemoryCache?.set(key, token, expiresAt);
await this.#oboRedisCache.set(key, token, expiresAt);
Expand All @@ -66,7 +77,9 @@ class OboSimpleCache {
return memoryHit?.token ?? null;
}

public set(key: string, token: string, expiresAt: number): void {
public getCached = this.get;

public async set(key: string, token: string, expiresAt: number): Promise<void> {
this.#oboMemoryCache.set(key, token, expiresAt);
}

Expand All @@ -78,3 +91,7 @@ class OboSimpleCache {
const hasRedis = REDIS_URI !== undefined && REDIS_USERNAME !== undefined && REDIS_PASSWORD !== undefined;

export const oboCache = hasRedis ? new OboTieredCache(REDIS_URI, REDIS_USERNAME, REDIS_PASSWORD) : new OboSimpleCache();

export const getCacheKey = (accessToken: string, appName: string) => `${hash(accessToken)}-${appName}`;

const hash = (str: string) => createHash('sha256').update(str).digest('hex');
6 changes: 2 additions & 4 deletions server/src/auth/on-behalf-of.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createHash } from 'node:crypto';
import { oboCache } from '@app/auth/cache/cache';
import { getCacheKey, oboCache } from '@app/auth/cache/cache';
import { NAIS_CLUSTER_NAME } from '@app/config/config';
import { getLogger } from '@app/logger';
import type { Client, GrantBody } from 'openid-client';
Expand All @@ -13,8 +12,7 @@ export const getOnBehalfOfAccessToken = async (
trace_id: string,
span_id: string,
): Promise<string> => {
const hash = createHash('sha256').update(accessToken).digest('hex');
const cacheKey = `${hash}-${appName}`;
const cacheKey = getCacheKey(accessToken, appName);
const token = await oboCache.get(cacheKey);

if (token !== null) {
Expand Down
2 changes: 1 addition & 1 deletion server/src/plugins/access-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const accessTokenPlugin = fastifyPlugin(
}
});
},
{ fastify: '4', name: ACCESS_TOKEN_PLUGIN_ID },
{ fastify: '5', name: ACCESS_TOKEN_PLUGIN_ID },
);

const getAccessToken = (req: FastifyRequest): string | undefined => {
Expand Down
2 changes: 1 addition & 1 deletion server/src/plugins/api-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const apiProxyPlugin = fastifyPlugin<ApiProxyPluginOptions>(
});
}
},
{ fastify: '4', name: API_PROXY_PLUGIN_ID, dependencies: [OBO_ACCESS_TOKEN_PLUGIN_ID, SERVER_TIMING_PLUGIN_ID] },
{ fastify: '5', name: API_PROXY_PLUGIN_ID, dependencies: [OBO_ACCESS_TOKEN_PLUGIN_ID, SERVER_TIMING_PLUGIN_ID] },
);

const prefixServerTimingEntry = (entry: string, appName: string): string => {
Expand Down
2 changes: 1 addition & 1 deletion server/src/plugins/client-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ export const clientVersionPlugin = fastifyPlugin(
}
});
},
{ fastify: '4', name: CLIENT_VERSION_PLUGIN_ID },
{ fastify: '5', name: CLIENT_VERSION_PLUGIN_ID },
);
2 changes: 1 addition & 1 deletion server/src/plugins/error-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ export const errorReportPlugin = fastifyPlugin(
reply.status(200).send();
});
},
{ fastify: '4', name: ERROR_REPORT_PLUGIN_ID },
{ fastify: '5', name: ERROR_REPORT_PLUGIN_ID },
);
41 changes: 25 additions & 16 deletions server/src/plugins/frontend-log/frontend-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const frontendLogPlugin = fastifyPlugin(
session_time: Type.Number({ description: 'Milliseconds since start of session.' }),
session_time_formatted: Type.String({
description: 'Formatted time since start of session.',
format: 'time',
format: 'session_time',
examples: ['1:35:45.987'],
}),
route: Type.String({
Expand All @@ -36,20 +36,29 @@ export const frontendLogPlugin = fastifyPlugin(
}),
Type.Union([
// Navigation event
Type.Object({
type: Type.Literal(FrontendEventTypes.NAVIGATION),
}),
Type.Object(
{
type: Type.Literal(FrontendEventTypes.NAVIGATION),
},
{ title: 'Navigation event' },
),
// App event
Type.Object({
type: Type.Literal(FrontendEventTypes.APP),
action: Type.String(),
}),
Type.Object(
{
type: Type.Literal(FrontendEventTypes.APP),
action: Type.String(),
},
{ title: 'App event' },
),
// Session event
Type.Object({
type: Type.Literal(FrontendEventTypes.SESSION),
message: Type.String(),
action: Type.Enum(SessionAction),
}),
Type.Object(
{
type: Type.Literal(FrontendEventTypes.SESSION),
message: Type.String(),
action: Type.Enum(SessionAction),
},
{ title: 'Session event' },
),
// API event
Type.Object(
{
Expand All @@ -58,7 +67,7 @@ export const frontendLogPlugin = fastifyPlugin(
response_time: Type.Number({ description: 'API response time in milliseconds.' }),
status: Type.Union([Type.Number(), Type.String()]),
},
{ description: 'API event.' },
{ title: 'API event' },
),
// Error event
Type.Object(
Expand All @@ -67,7 +76,7 @@ export const frontendLogPlugin = fastifyPlugin(
message: Type.String(),
stack: Type.Optional(Type.String({ description: 'Stack trace of the error.' })),
},
{ description: 'Error event.' },
{ title: 'Error event' },
),
]),
]),
Expand All @@ -82,5 +91,5 @@ export const frontendLogPlugin = fastifyPlugin(
},
);
},
{ fastify: '4', name: FRONTEND_LOG_PLUGIN_ID },
{ fastify: '5', name: FRONTEND_LOG_PLUGIN_ID },
);
2 changes: 1 addition & 1 deletion server/src/plugins/health.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ export const healthPlugin = fastifyPlugin(
return reply.status(200).type('text/plain').send('Ready');
});
},
{ fastify: '4', name: HEALTH_PLUGIN_ID },
{ fastify: '5', name: HEALTH_PLUGIN_ID },
);
2 changes: 1 addition & 1 deletion server/src/plugins/http-logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const httpLoggerPlugin = fastifyPlugin(
});
},
{
fastify: '4',
fastify: '5',
name: HTTP_LOGGER_PLUGIN_ID,
dependencies: [PROXY_VERSION_PLUGIN_ID, SERVE_INDEX_PLUGIN_ID],
},
Expand Down
2 changes: 1 addition & 1 deletion server/src/plugins/local-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,5 @@ export const localDevPlugin = fastifyPlugin(
return res.header('content-type', mimeType).status(200).send(data);
});
},
{ fastify: '4', name: LOCAL_DEV_PLUGIN_ID },
{ fastify: '5', name: LOCAL_DEV_PLUGIN_ID },
);
2 changes: 1 addition & 1 deletion server/src/plugins/not-found.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const notFoundPlugin = fastifyPlugin(
return reply.status(404).header('content-type', 'text/html').send(DEV_404_HTML);
});
},
{ fastify: '4', name: NOT_FOUND_PLUGIN_ID, dependencies: [SERVE_INDEX_PLUGIN_ID] },
{ fastify: '5', name: NOT_FOUND_PLUGIN_ID, dependencies: [SERVE_INDEX_PLUGIN_ID] },
);

const getCells = (ytelse: string): string[] =>
Expand Down
89 changes: 38 additions & 51 deletions server/src/plugins/obo-token.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getCacheKey, oboCache } from '@app/auth/cache/cache';
import { oboRequestDuration } from '@app/auth/cache/cache-gauge';
import { getOnBehalfOfAccessToken } from '@app/auth/on-behalf-of';
import { getTokenXClient } from '@app/auth/token-x-client';
Expand All @@ -11,91 +12,77 @@ import fastifyPlugin from 'fastify-plugin';

const log = getLogger('obo-token-plugin');

const oboAccessTokenMapKey = Symbol('oboAccessTokenMap');

declare module 'fastify' {
interface FastifyRequest {
[oboAccessTokenMapKey]: Map<string, string>;
getOboAccessToken(appName: string, reply: FastifyReply): Promise<string | undefined>;
/** OBO token cache. */
oboAccessTokenMap: Map<string, string>;
/** Gets OBO token and caches it. */
getOboAccessToken(appName: string, reply?: FastifyReply): Promise<string | undefined>;
getCachedOboAccessToken(appName: string): string | undefined;
}
}

const NOOP = async () => undefined;
const ASYNC_NOOP = async () => undefined;
const SYNC_NOOP = () => undefined;

export const OBO_ACCESS_TOKEN_PLUGIN_ID = 'obo-access-token';

export const oboAccessTokenPlugin = fastifyPlugin(
async (app) => {
app.decorateRequest(oboAccessTokenMapKey, null);
app.decorateRequest('oboAccessTokenMap');

app.addHook('onRequest', async (req): Promise<void> => {
req[oboAccessTokenMapKey] = new Map();
req.oboAccessTokenMap = new Map();
});

if (isDeployed) {
app.decorateRequest('getOboAccessToken', async function (appName: string, reply: FastifyReply) {
const cachedOboAccessToken = getCachedOboAccessToken(appName, this);

if (cachedOboAccessToken !== undefined) {
log.debug({
msg: `Using cached OBO token for "${appName}".`,
trace_id: this.trace_id,
span_id: this.span_id,
client_version: this.client_version,
data: { route: this.url },
});

return cachedOboAccessToken;
app.decorateRequest('getOboAccessToken', async function (appName: string, reply?: FastifyReply) {
const requestOboAccessToken = this.oboAccessTokenMap.get(appName);

if (requestOboAccessToken !== undefined) {
return requestOboAccessToken;
}

const oboAccessToken = await getOboToken(appName, this, reply);

if (oboAccessToken !== undefined) {
log.debug({
msg: `Adding OBO token for "${appName}". Had ${this[oboAccessTokenMapKey].size} before.`,
trace_id: this.trace_id,
span_id: this.span_id,
client_version: this.client_version,
data: { route: this.url },
});

this[oboAccessTokenMapKey].set(appName, oboAccessToken);
this.oboAccessTokenMap.set(appName, oboAccessToken);
} else {
this.oboAccessTokenMap.delete(appName);
}

return oboAccessToken;
});

app.decorateRequest('getCachedOboAccessToken', function (appName: string) {
return (
this.oboAccessTokenMap.get(appName) ?? oboCache.getCached(getCacheKey(this.accessToken, appName)) ?? undefined
);
});
} else {
app.decorateRequest('getOboAccessToken', NOOP);
app.decorateRequest('getOboAccessToken', ASYNC_NOOP);
app.decorateRequest('getCachedOboAccessToken', SYNC_NOOP);
}

app.decorateRequest('getCachedOboAccessToken', function (appName: string) {
return getCachedOboAccessToken(appName, this);
});
},
{
fastify: '4',
fastify: '5',
name: OBO_ACCESS_TOKEN_PLUGIN_ID,
dependencies: [ACCESS_TOKEN_PLUGIN_ID, SERVER_TIMING_PLUGIN_ID],
},
);

const getCachedOboAccessToken = (appName: string, req: FastifyRequest) => {
log.debug({
msg: `Getting OBO token for "${appName}". Has ${req[oboAccessTokenMapKey].size} tokens.`,
trace_id: req.trace_id,
span_id: req.span_id,
client_version: req.client_version,
data: { route: req.url },
});

return req[oboAccessTokenMapKey].get(appName);
};

type GetOboToken = (appName: string, req: FastifyRequest, reply: FastifyReply) => Promise<string | undefined>;
type GetOboToken = (appName: string, req: FastifyRequest, reply?: FastifyReply) => Promise<string | undefined>;

const getOboToken: GetOboToken = async (appName, req, reply) => {
const { trace_id, span_id, accessToken } = req;
const { trace_id, span_id, accessToken, url, client_version } = req;

log.debug({
msg: `Getting OBO token for "${appName}".`,
trace_id,
span_id,
client_version,
data: { route: url },
});

if (accessToken.length === 0) {
return undefined;
Expand All @@ -104,14 +91,14 @@ const getOboToken: GetOboToken = async (appName, req, reply) => {
try {
const tokenXClientStart = performance.now();
const authClient = await getTokenXClient();
reply.addServerTiming('token_x_client_middleware', getDuration(tokenXClientStart), 'TokenX Client Middleware');
reply?.addServerTiming('token_x_client_middleware', getDuration(tokenXClientStart), 'TokenX Client Middleware');

const oboStart = performance.now();
const oboAccessToken = await getOnBehalfOfAccessToken(authClient, accessToken, appName, trace_id, span_id);

const duration = getDuration(oboStart);
oboRequestDuration.observe(duration);
reply.addServerTiming('obo_token_middleware', duration, 'OBO Token Middleware');
reply?.addServerTiming('obo_token_middleware', duration, 'OBO Token Middleware');

return oboAccessToken;
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion server/src/plugins/proxy-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ export const proxyVersionPlugin = fastifyPlugin(
reply.header(PROXY_VERSION_HEADER, PROXY_VERSION);
});
},
{ fastify: '4', name: PROXY_VERSION_PLUGIN_ID },
{ fastify: '5', name: PROXY_VERSION_PLUGIN_ID },
);
2 changes: 1 addition & 1 deletion server/src/plugins/serve-index/serve-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ export const serveIndexPlugin = fastifyPlugin(
app.get(path, serveIndexHandler);
}
},
{ fastify: '4', name: SERVE_INDEX_PLUGIN_ID, dependencies: [API_PROXY_PLUGIN_ID, LOCAL_DEV_PLUGIN_ID] },
{ fastify: '5', name: SERVE_INDEX_PLUGIN_ID, dependencies: [API_PROXY_PLUGIN_ID, LOCAL_DEV_PLUGIN_ID] },
);
Loading

0 comments on commit df6e9b1

Please sign in to comment.