Skip to content

Commit edd4917

Browse files
committed
Generate overview on interval, organize code
1 parent 0223afc commit edd4917

File tree

7 files changed

+293
-243
lines changed

7 files changed

+293
-243
lines changed

server/src/config/env.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,5 @@ export const NAIS_NAMESPACE = requiredEnvString('NAIS_NAMESPACE', 'none');
3535

3636
export const POD_NAME = requiredEnvString('OTEL_RESOURCE_ATTRIBUTES_POD_NAME', 'none');
3737

38-
export const YTELSE_OVERVIEW_URL = isDeployedToProd
39-
? 'https://www.nav.no/klage'
40-
: 'https://www.ekstern.dev.nav.no/klage';
38+
export const NAV_KLAGE_URL_PROD = 'https://www.nav.no/klage';
39+
export const NAV_KLAGE_URL_DEV = 'https://www.ekstern.dev.nav.no/klage';

server/src/plugins/not-found.ts

-239
This file was deleted.

server/src/plugins/not-found/html.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const html = (strings: TemplateStringsArray, ...values: unknown[]): string =>
2+
String.raw({ raw: strings }, ...values);
+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { NAV_KLAGE_URL_DEV, NAV_KLAGE_URL_PROD, isDeployed, isDeployedToProd } from '@app/config/env';
2+
import { INNSENDINGSYTELSER, Innsendingsytelse } from '@app/innsendingsytelser';
3+
import { getLogger } from '@app/logger';
4+
import { html } from '@app/plugins/not-found/html';
5+
import { overviewCss } from '@app/plugins/not-found/overview-css';
6+
import { generateHtml } from '@app/plugins/not-found/overview-template';
7+
import { externalRedirectCounter } from '@app/plugins/serve-index/counters';
8+
import { removeSaksnummer } from '@app/plugins/serve-index/remove-saksnummer';
9+
import { CASE_TYPES } from '@app/plugins/serve-index/segments';
10+
import { SERVE_INDEX_PLUGIN_ID } from '@app/plugins/serve-index/serve-index';
11+
import fastifyPlugin from 'fastify-plugin';
12+
13+
const log = getLogger('not-found-plugin');
14+
15+
export const NOT_FOUND_PLUGIN_ID = 'not-found';
16+
17+
const HARMLESS_NOT_FOUND_PATHS = ['/favicon.ico', '/'];
18+
19+
export const notFoundPlugin = fastifyPlugin(
20+
async (app) => {
21+
app.setNotFoundHandler((req, reply) => {
22+
if (isDeployedToProd) {
23+
const harmless = HARMLESS_NOT_FOUND_PATHS.includes(req.url);
24+
25+
log[harmless ? 'debug' : 'warn']({
26+
msg: `Invalid URL. Redirecting to external URL ${NAV_KLAGE_URL_PROD}`,
27+
data: {
28+
url: req.url,
29+
referer: req.headers.referer ?? 'undefined',
30+
referrer: req.headers.referrer ?? 'undefined',
31+
},
32+
});
33+
34+
externalRedirectCounter.inc({
35+
url: removeSaksnummer(req.url),
36+
});
37+
38+
return reply.redirect(NAV_KLAGE_URL_PROD);
39+
}
40+
41+
return reply.status(404).header('content-type', 'text/html').send(OVERVIEW_PAGE_HTML);
42+
});
43+
},
44+
{ fastify: '5', name: NOT_FOUND_PLUGIN_ID, dependencies: [SERVE_INDEX_PLUGIN_ID] },
45+
);
46+
47+
const createLink = (path: string, type: string) => html`<a class="capitalized link" href="${path}">${type}</a>`;
48+
49+
const getLinks = (ytelse: string): string[] =>
50+
CASE_TYPES.flatMap((type) => [
51+
createLink(`/nb/${type}/${ytelse}`, type),
52+
createLink(`/nb/ettersendelse/${type}/${ytelse}`, `Ettersendelse ${type}`),
53+
]);
54+
55+
const HIDDEN_YTELSER = [
56+
Innsendingsytelse.FORSIKRING,
57+
Innsendingsytelse.HJELPEMIDLER_ORTOPEDISKE,
58+
Innsendingsytelse.MEDLEMSKAP,
59+
];
60+
61+
const getRows = async (): Promise<string[][]> => {
62+
const nb = await getYtelseNames('nb');
63+
const nn = await getYtelseNames('nn');
64+
const en = await getYtelseNames('en');
65+
66+
return INNSENDINGSYTELSER.filter((y) => !HIDDEN_YTELSER.includes(y)).map((ytelse) => [
67+
html`<td class="key-cell">${ytelse}</td>`,
68+
html`<td>
69+
<dl class="two-columns-grid">
70+
<dt>Bokmål:</dt><dd>${nb.find(({ id }) => id === ytelse)?.navn ?? ytelse}</dd>
71+
<dt>Nynorsk:</dt><dd>${nn.find(({ id }) => id === ytelse)?.navn ?? ytelse}</dd>
72+
<dt>Engelsk:</dt><dd>${en.find(({ id }) => id === ytelse)?.navn ?? ytelse}</dd>
73+
</dl>
74+
</td>`,
75+
html`<td><div class="two-columns-grid">${getLinks(ytelse).join('\n')}</div></td>`,
76+
]);
77+
};
78+
79+
const KODEVERK_DOMAIN = isDeployed ? 'http://klage-kodeverk-api' : 'https://klage-kodeverk-api.intern.dev.nav.no';
80+
81+
interface YtelseName {
82+
id: Innsendingsytelse;
83+
navn: string;
84+
}
85+
86+
const isYtelsResponse = (data: unknown): data is YtelseName[] =>
87+
Array.isArray(data) &&
88+
data.every((item) => typeof item === 'object' && item !== null && 'id' in item && 'navn' in item);
89+
90+
const getYtelseNames = async (lang: 'nb' | 'nn' | 'en'): Promise<YtelseName[]> => {
91+
const res = await fetch(`${KODEVERK_DOMAIN}/kodeverk/innsendingsytelser/${lang}`);
92+
const data = await res.json();
93+
94+
if (!isYtelsResponse(data)) {
95+
throw new Error('Invalid response from kodeverk-api');
96+
}
97+
98+
return data;
99+
};
100+
101+
const generateOverviewPage = async () => {
102+
const rowData = await getRows();
103+
104+
return generateHtml({
105+
rows: rowData.map((row) => html`<tr>${row.join('')}</tr>`).join(''),
106+
css: overviewCss,
107+
url: NAV_KLAGE_URL_DEV,
108+
});
109+
};
110+
111+
let OVERVIEW_PAGE_HTML: string | null = isDeployedToProd ? null : await generateOverviewPage();
112+
113+
if (!isDeployedToProd) {
114+
// Refresh the overview page.
115+
setInterval(
116+
async () => {
117+
try {
118+
const newHtml = await generateOverviewPage();
119+
120+
if (newHtml !== OVERVIEW_PAGE_HTML) {
121+
OVERVIEW_PAGE_HTML = newHtml;
122+
log.debug({ msg: 'Successfully updated the Overview page' });
123+
} else {
124+
log.debug({ msg: 'Overview page is up to date' });
125+
}
126+
} catch (error) {
127+
log.error({ msg: 'Error generating overview page', error });
128+
}
129+
},
130+
5 * 60 * 1_000,
131+
);
132+
}

0 commit comments

Comments
 (0)