Skip to content

Commit

Permalink
Merge branch 'main' into SW-233-update-translations
Browse files Browse the repository at this point in the history
  • Loading branch information
j-maynard authored Oct 16, 2024
2 parents f257448 + 0ea2c82 commit 8568cf4
Show file tree
Hide file tree
Showing 12 changed files with 93 additions and 80 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"test:ci": "APP_ENV=ci jest --ci --coverage --config=jest.config.ts",
"check": "npm-run-all prettier:fix lint:fix test build",
"dev:check": "npm-run-all check dev",
"predev": "f() { if [ -x $(command -v podman) ]; then podman compose up -d redis; else docker compose up -d redis; fi }; f",
"predev": "f() { if [ $(command -v podman) ]; then podman compose up -d redis; else docker compose up -d redis; fi }; f",
"dev": "APP_ENV=local nodemon --watch src -e ts,ejs --exec npm run start",
"start": "npm run build && node dist/server.js | pino-colada",
"start:container": "node dist/server.js"
Expand Down
6 changes: 6 additions & 0 deletions src/@types/express/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AuthenticatedRequest } from '../../interfaces/authenticated-request';
import { ServiceContainer } from '../../interfaces/service-container';

declare module 'express-serve-static-core' {
interface Request extends ServiceContainer, AuthenticatedRequest {}
}
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { dataset } from './routes/dataset';
import { errorHandler } from './routes/error-handler';
import { homepage } from './routes/homepage';
import { notFound } from './routes/not-found';
import { initServices } from './middleware/services';

const app: Application = express();
const config = appConfig();
Expand All @@ -38,6 +39,7 @@ app.use(session);
app.use(i18nextMiddleware.handle(i18next));
app.use(skipMap);
app.use(languageSwitcher);
app.use(initServices);

// configure the view engine
app.set('views', path.join(__dirname, 'views'));
Expand Down
5 changes: 0 additions & 5 deletions src/interfaces/authed-request.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/interfaces/authenticated-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { User } from './user.interface';

export interface AuthenticatedRequest {
user?: User;
jwt?: string;
}
7 changes: 7 additions & 0 deletions src/interfaces/service-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Locale } from '../enums/locale';
import { StatsWalesApi } from '../services/stats-wales-api';

export interface ServiceContainer {
swapi: StatsWalesApi;
buildUrl: (path: string, locale: Locale, query?: Record<string, string>) => string;
}
5 changes: 2 additions & 3 deletions src/middleware/ensure-authenticated.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { RequestHandler } from 'express';
import { Request, Response, NextFunction } from 'express';
import JWT from 'jsonwebtoken';

import { AuthedRequest } from '../interfaces/authed-request';
import { JWTPayloadWithUser } from '../interfaces/jwt-payload-with-user';
import { logger } from '../utils/logger';
import { appConfig } from '../config';
Expand All @@ -11,7 +10,7 @@ import { localeUrl } from './language-switcher';

const config = appConfig();

export const ensureAuthenticated: RequestHandler = (req: AuthedRequest, res, next) => {
export const ensureAuthenticated = (req: Request, res: Response, next: NextFunction) => {
logger.debug(`checking if user is authenticated for route ${req.originalUrl}...`);

const locale = req.language as Locale;
Expand Down
10 changes: 7 additions & 3 deletions src/middleware/language-switcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import { isEmpty, omit } from 'lodash';
import { logger } from '../utils/logger';
import { Locale } from '../enums/locale';

import { ignoreRoutes, SUPPORTED_LOCALES } from './translation';
import { ignoreRoutes, SUPPORTED_LOCALES, i18next } from './translation';

export const localeUrl = (path: string, locale: Locale, query?: Record<string, string>): string => {
const locales = SUPPORTED_LOCALES as string[];

// TODO: translate URL path, params? to Welsh language
const pathElements = path
let pathElements = path
.split('/')
.filter(Boolean) // strip empty elements to avoid trailing slash
.filter((element) => !locales.includes(element)); // strip language from the path if present

if (![Locale.English, Locale.EnglishGb].includes(locale)) {
// translate the url path to the new locale
pathElements = pathElements.map((element) => i18next.t(`routes.${element}`, { lng: locale }));
}

const newPath = isEmpty(pathElements) ? '' : `/${pathElements.join('/')}`;
const queryString = isEmpty(query) ? '' : `?${new URLSearchParams(query).toString()}`;

Expand Down
17 changes: 17 additions & 0 deletions src/middleware/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NextFunction, Request, Response } from 'express';

import { Locale } from '../enums/locale';
import { StatsWalesApi } from '../services/stats-wales-api';

import { localeUrl } from './language-switcher';

// initialise any request-scoped services required by the app and store them on the request object for later use
// see interfaces/service-container.ts and @types/express/index.d.ts for details
export const initServices = (req: Request, res: Response, next: NextFunction): void => {
if (!/^\/(public|css|assets)/.test(req.originalUrl)) {
req.swapi = new StatsWalesApi(req.language as Locale, req.cookies.jwt);
req.buildUrl = localeUrl; // for controllers
res.locals.buildUrl = localeUrl; // for templates
}
next();
};
3 changes: 1 addition & 2 deletions src/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import JWT from 'jsonwebtoken';
import { logger } from '../utils/logger';
import { JWTPayloadWithUser } from '../interfaces/jwt-payload-with-user';
import { appConfig } from '../config';
import { AuthedRequest } from '../interfaces/authed-request';

export const auth = Router();

Expand Down Expand Up @@ -59,7 +58,7 @@ auth.get('/callback', (req: Request, res: Response) => {
res.redirect(`/${req.language}`);
});

auth.get('/logout', (req: AuthedRequest, res: Response) => {
auth.get('/logout', (req: Request, res: Response) => {
logger.debug('logging out user');
res.clearCookie('jwt', { domain: cookieDomain });
res.redirect(`/${req.language}/auth/login`);
Expand Down
11 changes: 5 additions & 6 deletions src/routes/dataset.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { Readable } from 'stream';

import { Router, Response, NextFunction } from 'express';
import { Router, Request, Response, NextFunction } from 'express';
import { validate as validateUUID } from 'uuid';

import { StatsWalesApi } from '../services/stats-wales-api';
import { FileList } from '../dtos/file-list';
import { ViewErrDTO } from '../dtos/view-dto';
import { logger } from '../utils/logger';
import { AuthedRequest } from '../interfaces/authed-request';
import { FileImportDTO } from '../dtos/dataset-dto';
import { Locale } from '../enums/locale';

export const dataset = Router();

const statsWalesApi = (req: AuthedRequest) => {
const statsWalesApi = (req: Request) => {
const lang = req.language as Locale;
const token = req.jwt;
return new StatsWalesApi(lang, token);
};

dataset.get('/', async (req: AuthedRequest, res: Response, next: NextFunction) => {
dataset.get('/', async (req: Request, res: Response, next: NextFunction) => {
try {
const fileList: FileList = await statsWalesApi(req).getFileList();
logger.debug(`FileList from server = ${JSON.stringify(fileList)}`);
Expand All @@ -29,7 +28,7 @@ dataset.get('/', async (req: AuthedRequest, res: Response, next: NextFunction) =
}
});

dataset.get('/:datasetId', async (req: AuthedRequest, res: Response) => {
dataset.get('/:datasetId', async (req: Request, res: Response) => {
const datasetId = req.params.datasetId;
const page: number = Number.parseInt(req.query.page_number as string, 10) || 1;
const page_size: number = Number.parseInt(req.query.page_size as string, 10) || 100;
Expand Down Expand Up @@ -63,7 +62,7 @@ dataset.get('/:datasetId', async (req: AuthedRequest, res: Response) => {
}
});

dataset.get('/:datasetId/import/:importId', async (req: AuthedRequest, res: Response) => {
dataset.get('/:datasetId/import/:importId', async (req: Request, res: Response) => {
if (!validateUUID(req.params.datasetId)) {
const err: ViewErrDTO = {
success: false,
Expand Down
Loading

0 comments on commit 8568cf4

Please sign in to comment.