refactor: eslint --fix

This commit is contained in:
b1ek 2024-07-27 21:27:38 +10:00
parent e6313080b9
commit 44eb3ceca4
Signed by: blek
GPG Key ID: 14546221E3595D0C
12 changed files with 336 additions and 260 deletions

View File

@ -1,9 +1,9 @@
import { KeysModule } from './keys/keys.module.js'; import { KeysModule } from './keys/keys.module.js'
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common'
import { ViewsController } from './views.controller.js'; import { ViewsController } from './views.controller.js'
@Module({ @Module({
controllers: [ ViewsController ], controllers: [ViewsController],
imports: [ KeysModule ] imports: [KeysModule],
}) })
export class AppModule {} export class AppModule {}

View File

@ -1,43 +1,55 @@
import { BadRequestException, Controller, Get, Query, Req } from "@nestjs/common"; import {
import { Address4, Address6 } from "ip-address"; BadRequestException,
Controller,
Get,
Query,
Req,
} from '@nestjs/common'
import { Address4, Address6 } from 'ip-address'
import type { FastifyRequest } from 'fastify'; import type { FastifyRequest } from 'fastify'
import { HKPOperation, VALID_OPS } from "../providers/abstract.provider.js"; import { HKPOperation, VALID_OPS } from '../providers/abstract.provider.js'
import { AllKeysProvider } from "../providers/all.provider.js"; import { AllKeysProvider } from '../providers/all.provider.js'
import { serializeIndexes } from "../indexes.js"; import { serializeIndexes } from '../indexes.js'
@Controller() @Controller()
export class HKPController { export class HKPController {
constructor(private allKeysProvider: AllKeysProvider) {}
constructor(
private allKeysProvider: AllKeysProvider
) {}
@Get('pks/lookup') @Get('pks/lookup')
async lookup(@Req() req: FastifyRequest, @Query('search') search: string, @Query('op') op: HKPOperation) { async lookup(
@Req() req: FastifyRequest,
@Query('search') search: string,
@Query('op') op: HKPOperation
) {
if (VALID_OPS.indexOf(op) === -1) { if (VALID_OPS.indexOf(op) === -1) {
throw new BadRequestException('op MUST be one of: ' + VALID_OPS.join(', ')); throw new BadRequestException(
'op MUST be one of: ' + VALID_OPS.join(', ')
)
} }
let ip: Address4 | Address6 | null = null; let ip: Address4 | Address6 | null = null
if (Address4.isValid(req.ip.replace(/^.*:/, ''))) { if (Address4.isValid(req.ip.replace(/^.*:/, ''))) {
ip = new Address4(req.ip.replace(/^.*:/, '')) ip = new Address4(req.ip.replace(/^.*:/, ''))
} else if (Address6.isValid(req.ip)) { } else if (Address6.isValid(req.ip)) {
ip = new Address6(req.ip) ip = new Address6(req.ip)
} }
if (ip === null) { if (ip === null) {
ip = new Address4('127.0.0.1'); ip = new Address4('127.0.0.1')
} }
const miscData = { ip } const miscData = { ip }
switch (op) { switch (op) {
case 'get': case 'get':
return this.allKeysProvider.get(search, miscData); return this.allKeysProvider.get(search, miscData)
case 'index': case 'index':
const indexes = await this.allKeysProvider.index(search, miscData); const indexes = await this.allKeysProvider.index(
return serializeIndexes(indexes); search,
miscData
)
return serializeIndexes(indexes)
} }
} }
} }

View File

@ -1,139 +1,173 @@
// as per https://www.ietf.org/archive/id/draft-gallagher-openpgp-hkp-05.html#name-machine-readable-indexes // as per https://www.ietf.org/archive/id/draft-gallagher-openpgp-hkp-05.html#name-machine-readable-indexes
export interface Index { export interface Index {
prefix: 'info' | 'pub' | 'uid'; prefix: 'info' | 'pub' | 'uid';
} }
export type Indexes = Index[]; export type Indexes = Index[];
export class InfoLine implements Index { export class InfoLine implements Index {
prefix: 'info'; prefix: 'info';
version: 1; version: 1;
count: number; count: number;
constructor(indexLine: string) { constructor(indexLine: string) {
parseIndex<InfoLine>(this, indexLine, [ 'version', 'count' ]); parseIndex<InfoLine>(this, indexLine, ['version', 'count']);
this.prefix = 'info'; this.prefix = 'info';
if (this.version != 1) { if (this.version != 1) {
throw new Error('InfoLine\'s version MUST be 1! Got ' + this.version) throw new Error("InfoLine's version MUST be 1! Got " + this.version);
}
if (this.count) {
if (typeof this.count !== 'number') {
if (isNaN(this.count)) {
throw new Error('InfoLine\'s count MUST NOT be NaN!');
}
}
}
} }
if (this.count) {
if (typeof this.count !== 'number') {
if (isNaN(this.count)) {
throw new Error("InfoLine's count MUST NOT be NaN!");
}
}
}
}
} }
export class PubLine implements Index { export class PubLine implements Index {
prefix: 'pub'; prefix: 'pub';
keyid?: string; keyid?: string;
keylen?: number; keylen?: number;
algorithm?: string; algorithm?: string;
creationdate?: number; creationdate?: number;
expirationdate?: number; expirationdate?: number;
flags?: string; flags?: string;
version?: string; version?: string;
constructor(indexLine: string) { constructor(indexLine: string) {
parseIndex<PubLine>(this, indexLine, [ 'keyid', 'keylen', 'algorithm', 'creationdate', 'expirationdate', 'flags', 'version' ]); parseIndex<PubLine>(this, indexLine, [
'keyid',
'keylen',
'algorithm',
'creationdate',
'expirationdate',
'flags',
'version',
]);
this.prefix = 'pub'; this.prefix = 'pub';
if (this.creationdate && typeof this.creationdate !== 'number') { if (this.creationdate && typeof this.creationdate !== 'number') {
this.creationdate = parseFloat(this.creationdate); this.creationdate = parseFloat(this.creationdate);
if (isNaN(this.creationdate)) { if (isNaN(this.creationdate)) {
throw new Error('PubLine\'s creationdate MUST NOT be NaN!'); throw new Error("PubLine's creationdate MUST NOT be NaN!");
} }
}
if (this.expirationdate && typeof this.expirationdate !== 'number') {
this.expirationdate = parseFloat(this.expirationdate);
if (isNaN(this.expirationdate)) {
throw new Error('PubLine\'s expirationdate MUST NOT be NaN!');
}
}
} }
if (this.expirationdate && typeof this.expirationdate !== 'number') {
this.expirationdate = parseFloat(this.expirationdate);
if (isNaN(this.expirationdate)) {
throw new Error("PubLine's expirationdate MUST NOT be NaN!");
}
}
}
} }
export class UidLine implements Index { export class UidLine implements Index {
prefix: 'uid'; prefix: 'uid';
uidstring?: string; uidstring?: string;
creationdate?: string; creationdate?: string;
expirationdate?: string; expirationdate?: string;
flags?: string; flags?: string;
constructor(indexLine: string) { constructor(indexLine: string) {
parseIndex<UidLine>(this, indexLine, [ 'uidstring', 'creationdate', 'expirationdate', 'flags' ]); parseIndex<UidLine>(this, indexLine, [
'uidstring',
'creationdate',
'expirationdate',
'flags',
]);
this.prefix = 'uid'; this.prefix = 'uid';
} }
} }
export function assertValidPrefix(prefix: string, throwError = true): boolean { export function assertValidPrefix(prefix: string, throwError = true): boolean {
if ([ 'info', 'pub', 'uid' ].indexOf(prefix) == -1) { if (['info', 'pub', 'uid'].indexOf(prefix) == -1) {
if (!throwError) { if (!throwError) {
return false; return false;
}
throw new Error('Prefix must be one of: info, pub, uid')
} }
return true throw new Error('Prefix must be one of: info, pub, uid');
}
return true;
} }
export function parseIndexes(untyped: string[]): Indexes { export function parseIndexes(untyped: string[]): Indexes {
return untyped return untyped
.filter(x => x.split(':').length > 1) .filter((x) => x.split(':').length > 1)
.filter(x => assertValidPrefix(x.split(':')[0], false)) .filter((x) => assertValidPrefix(x.split(':')[0], false))
.map(x => { .map((x) => {
const prefix = x.split(':')[0] as 'info' | 'pub' | 'uid'; const prefix = x.split(':')[0] as 'info' | 'pub' | 'uid';
switch (prefix) { switch (prefix) {
case 'info': return new InfoLine(x) case 'info': return new InfoLine(x)
case 'pub': return new PubLine(x) case 'pub': return new PubLine(x)
case 'uid': return new UidLine(x) case 'uid': return new UidLine(x)
} }
}) });
} }
export function parseIndex<T extends Index>(self: any, index: string, keys: (keyof T)[]): void { export function parseIndex<T extends Index>(
let exploded = index.replaceAll('\r', '').split(':'); // eslint-disable-next-line @typescript-eslint/no-explicit-any
self: any,
index: string,
keys: (keyof T)[],
): void {
let exploded = index.replaceAll('\r', '').split(':');
if (keys.length > exploded.length) { if (keys.length > exploded.length) {
throw new Error('keys MUST NOT be longer than index'); throw new Error('keys MUST NOT be longer than index');
} }
const prefix = exploded[0] as 'info' | 'pub' | 'uid'; const prefix = exploded[0] as 'info' | 'pub' | 'uid';
assertValidPrefix(prefix, true); assertValidPrefix(prefix, true);
self.prefix = prefix; self.prefix = prefix;
exploded = exploded.slice(1, exploded.length); exploded = exploded.slice(1, exploded.length);
for (let i = 0; i != keys.length; i++) { for (let i = 0; i != keys.length; i++) {
self[keys[i]] = decodeURIComponent(exploded[i]); self[keys[i]] = decodeURIComponent(exploded[i]);
} }
} }
export function serializeIndexes(indexes: Indexes): string { export function serializeIndexes(indexes: Indexes): string {
let out: (string | number | undefined)[][] = []; const out: (string | number | undefined)[][] = [];
for (const index of indexes) { for (const index of indexes) {
if (index instanceof InfoLine) { if (index instanceof InfoLine) {
out.push([ 'info', index.version, index.count ]); out.push(['info', index.version, index.count]);
}
if (index instanceof PubLine) {
out.push([ 'pub', index.keyid, index.algorithm, index.keylen, index.creationdate, index.expirationdate, index.flags, index.version ]);
}
if (index instanceof UidLine) {
out.push([ 'uid', index.uidstring, index.creationdate, index.expirationdate, index.flags ]);
}
} }
if (index instanceof PubLine) {
out.push([
'pub',
index.keyid,
index.algorithm,
index.keylen,
index.creationdate,
index.expirationdate,
index.flags,
index.version,
]);
}
if (index instanceof UidLine) {
out.push([
'uid',
index.uidstring,
index.creationdate,
index.expirationdate,
index.flags,
]);
}
}
return out return (
.map( out
x => x .map((x) =>
.map(x => x ?? '') x.map(x => x ?? '')
.map(encodeURIComponent) .map(encodeURIComponent)
.join(':') .join(':')
) )
.join('\n') + '\n' .join('\n') + '\n'
);
} }

View File

@ -1,11 +1,11 @@
import { Module } from "@nestjs/common"; import { Module } from '@nestjs/common'
import { HKPController } from "./controllers/hkp.controller.js"; import { HKPController } from './controllers/hkp.controller.js'
import { OpenPGPKeysProvider } from "./providers/openpgp.provider.js"; import { OpenPGPKeysProvider } from './providers/openpgp.provider.js'
import { AllKeysProvider } from "./providers/all.provider.js"; import { AllKeysProvider } from './providers/all.provider.js'
@Module({ @Module({
providers: [ OpenPGPKeysProvider, AllKeysProvider ], providers: [OpenPGPKeysProvider, AllKeysProvider],
controllers: [ HKPController ], controllers: [HKPController],
exports: [ OpenPGPKeysProvider, AllKeysProvider ] exports: [OpenPGPKeysProvider, AllKeysProvider],
}) })
export class KeysModule {} export class KeysModule {}

View File

@ -1,18 +1,20 @@
import { Get, Injectable } from '@nestjs/common'; import { Get, Injectable } from '@nestjs/common'
import { Indexes } from '../indexes.js'; import { Indexes } from '../indexes.js'
import { Address4, Address6 } from 'ip-address'; import { Address4, Address6 } from 'ip-address'
export type AdditionalData = { ip: Address4 | Address6 }; export type AdditionalData = { ip: Address4 | Address6 }
export type HKPOperation = keyof AbstractKeysProvider; export type HKPOperation = keyof AbstractKeysProvider
export type GetOperationReturn = string | 404; export type GetOperationReturn = string | 404
export const VALID_OPS: readonly HKPOperation[] = Object.freeze([ 'get', 'index' ]) export const VALID_OPS: readonly HKPOperation[] = Object.freeze([
'get',
'index',
])
/** https://www.ietf.org/archive/id/draft-gallagher-openpgp-hkp-05.html#name-the-op-operation-field */ /** https://www.ietf.org/archive/id/draft-gallagher-openpgp-hkp-05.html#name-the-op-operation-field */
@Injectable() @Injectable()
export abstract class AbstractKeysProvider { export abstract class AbstractKeysProvider {
readonly url = Object.freeze('http://none')
readonly url = Object.freeze('http://none');
/** /**
The "get" operation requests keys from the keyserver by textual search. A string that specifies which key(s) to return is provided in the "search" field. The "get" operation requests keys from the keyserver by textual search. A string that specifies which key(s) to return is provided in the "search" field.
@ -24,8 +26,15 @@ export abstract class AbstractKeysProvider {
If no keys match the request, the keyserver SHOULD return an appropriate HTTP error code such as 404 ("Not Found"). If no keys match the request, the keyserver SHOULD return an appropriate HTTP error code such as 404 ("Not Found").
*/ */
@Get() @Get()
async get(search: string, data: AdditionalData): Promise<GetOperationReturn> { return 404 } async get(
search: string,
data: AdditionalData
): Promise<GetOperationReturn> {
return 404
}
@Get() @Get()
async index(search: string, data: AdditionalData): Promise<Indexes> { return [] } async index(search: string, data: AdditionalData): Promise<Indexes> {
return []
}
} }

View File

@ -1,7 +1,10 @@
import { Get, Injectable } from "@nestjs/common"; import { Get, Injectable } from '@nestjs/common'
import { AbstractKeysProvider, type AdditionalData } from "./abstract.provider.js"; import {
import { Indexes, InfoLine } from "../indexes.js"; AbstractKeysProvider,
import { OpenPGPKeysProvider } from "./openpgp.provider.js"; type AdditionalData,
} from './abstract.provider.js'
import { Indexes, InfoLine } from '../indexes.js'
import { OpenPGPKeysProvider } from './openpgp.provider.js'
/** /**
* This provider searches all key providers and returns their combined result * This provider searches all key providers and returns their combined result
@ -9,40 +12,41 @@ import { OpenPGPKeysProvider } from "./openpgp.provider.js";
@Injectable() @Injectable()
export class AllKeysProvider implements AbstractKeysProvider { export class AllKeysProvider implements AbstractKeysProvider {
constructor( readonly url = Object.freeze('http://none');
private openPgpKeysProvider: OpenPGPKeysProvider
) {} constructor(private openPgpKeysProvider: OpenPGPKeysProvider) {}
getAll(): AbstractKeysProvider[] { getAll(): AbstractKeysProvider[] {
return [ return [this.openPgpKeysProvider]
this.openPgpKeysProvider
]
} }
@Get() @Get()
async get(search: string, data: AdditionalData): Promise<string | 404> { async get(search: string, data: AdditionalData): Promise<string | 404> {
const all = this.getAll(); const all = this.getAll()
const promises = await Promise.all(all.map(x => x.get(search, data))) const promises = await Promise.all(all.map((x) => x.get(search, data)))
if (promises.filter(x => x == 404).length == promises.length) { // all failed if (promises.filter((x) => x == 404).length == promises.length) {
// all failed
return 404 return 404
} else { } else {
// if there are multiple keys, join them together to avoid missing data // if there are multiple keys, join them together to avoid missing data
return promises return promises.filter((x) => typeof x === 'string').join('\n')
.filter(x => typeof x === 'string')
.join('\n')
} }
} }
@Get() @Get()
async index(search: string, data: AdditionalData): Promise<Indexes> { async index(search: string, data: AdditionalData): Promise<Indexes> {
const all = this.getAll(); const all = this.getAll()
const promises = await Promise.all(all.map(x => x.index(search, data))) const promises = await Promise.all(
all.map((x) => x.index(search, data))
)
// merge indexes if there are multiple // merge indexes if there are multiple
const out = [ new InfoLine('info:1:1') ] as Indexes; const out = [new InfoLine('info:1:1')] as Indexes
promises.forEach(x => x.filter(x => x.prefix !== 'info').forEach(y => out.push(y))); promises.forEach((x) =>
x.filter((x) => x.prefix !== 'info').forEach((y) => out.push(y))
)
return out; return out
} }
} }

View File

@ -1,22 +1,21 @@
import { Get, Injectable } from "@nestjs/common"; import { Get, Injectable } from '@nestjs/common'
import { AbstractKeysProvider } from "./abstract.provider.js"; import { AbstractKeysProvider } from './abstract.provider.js'
import type { AdditionalData } from "./abstract.provider.js"; import type { AdditionalData } from './abstract.provider.js'
import { proxyGetOp, proxyIndexOp } from "./utils.js"; import { proxyGetOp, proxyIndexOp } from './utils.js'
import { Indexes } from "../indexes.js"; import { Indexes } from '../indexes.js'
@Injectable() @Injectable()
export class OpenPGPKeysProvider implements AbstractKeysProvider { export class OpenPGPKeysProvider implements AbstractKeysProvider {
readonly url = Object.freeze('https://keys.openpgp.org')
readonly url = Object.freeze('https://keys.openpgp.org');
@Get() @Get()
async get(search: string, data: AdditionalData): Promise<string | 404> { async get(search: string, data: AdditionalData): Promise<string | 404> {
return proxyGetOp(this.url + '/pks/lookup', search, data); return proxyGetOp(this.url + '/pks/lookup', search, data)
} }
@Get() @Get()
async index(search: string, data: AdditionalData): Promise<Indexes> { async index(search: string, data: AdditionalData): Promise<Indexes> {
return proxyIndexOp(this.url + '/pks/lookup', search, data); return proxyIndexOp(this.url + '/pks/lookup', search, data)
} }
} }

View File

@ -1,22 +1,21 @@
import { Get, Injectable } from "@nestjs/common"; import { Get, Injectable } from '@nestjs/common'
import { AbstractKeysProvider } from "./abstract.provider.js"; import { AbstractKeysProvider } from './abstract.provider.js'
import type { AdditionalData } from "./abstract.provider.js"; import type { AdditionalData } from './abstract.provider.js'
import { proxyGetOp, proxyIndexOp } from "./utils.js"; import { proxyGetOp, proxyIndexOp } from './utils.js'
import { Indexes } from "../indexes.js"; import { Indexes } from '../indexes.js'
@Injectable() @Injectable()
export class UbuntuKeysProvider implements AbstractKeysProvider { export class UbuntuKeysProvider implements AbstractKeysProvider {
readonly url = Object.freeze('https://keyserver.ubuntu.com')
readonly url = Object.freeze('https://keyserver.ubuntu.com');
@Get() @Get()
async get(search: string, data: AdditionalData): Promise<string | 404> { async get(search: string, data: AdditionalData): Promise<string | 404> {
return proxyGetOp(this.url + '/pks/lookup', search, data); return proxyGetOp(this.url + '/pks/lookup', search, data)
} }
@Get() @Get()
async index(search: string, data: AdditionalData): Promise<Indexes> { async index(search: string, data: AdditionalData): Promise<Indexes> {
return proxyIndexOp(this.url + '/pks/lookup', search, data); return proxyIndexOp(this.url + '/pks/lookup', search, data)
} }
} }

View File

@ -1,47 +1,63 @@
import ky, { ResponsePromise } from "ky"; import ky, { ResponsePromise } from 'ky'
import { AdditionalData, GetOperationReturn, HKPOperation } from "./abstract.provider.js"; import {
import { Indexes, parseIndexes } from "../indexes.js"; AdditionalData,
GetOperationReturn,
HKPOperation,
} from './abstract.provider.js'
import { Indexes, parseIndexes } from '../indexes.js'
const BEGIN_HEADER = '-----BEGIN PGP PUBLIC KEY BLOCK-----'; const BEGIN_HEADER = '-----BEGIN PGP PUBLIC KEY BLOCK-----'
const END_HEADER = '-----END PGP PUBLIC KEY BLOCK-----'; const END_HEADER = '-----END PGP PUBLIC KEY BLOCK-----'
export function getKey(raw: string): string { export function getKey(raw: string): string {
return ( return raw
raw .replace(new RegExp(`.*${BEGIN_HEADER}`, 'gm'), BEGIN_HEADER)
.replace(new RegExp(`.*${BEGIN_HEADER}`, 'gm'), BEGIN_HEADER) .replace(new RegExp(`${END_HEADER}.*`, 'gm'), END_HEADER)
.replace(new RegExp(`${END_HEADER}.*`, 'gm'), END_HEADER)
);
} }
/** /**
* Proxy request to a 3rd party keyserver via ky * Proxy request to a 3rd party keyserver via ky
*/ */
export function proxyRequest(url: string, op: HKPOperation, search: string, data: AdditionalData): ResponsePromise { export function proxyRequest(
return ky( url: string,
url, op: HKPOperation,
{ search: string,
headers: { data: AdditionalData
'User-Agent': 'Cupid (https://cupid.blek.codes)' ): ResponsePromise {
}, return ky(url, {
searchParams: { headers: {
op, 'User-Agent': 'Cupid (https://cupid.blek.codes)',
search },
} searchParams: {
} op,
) search,
},
})
} }
export async function proxyGetOp(url: string, search: string, data: AdditionalData): Promise<GetOperationReturn> { export async function proxyGetOp(
const httpRes = await proxyRequest(url, 'get', search, data); url: string,
if (httpRes.status !== 200) { return 404 } search: string,
data: AdditionalData
): Promise<GetOperationReturn> {
const httpRes = await proxyRequest(url, 'get', search, data)
if (httpRes.status !== 200) {
return 404
}
return getKey(await httpRes.text()) return getKey(await httpRes.text())
} }
export async function proxyIndexOp(url: string, search: string, data: AdditionalData): Promise<Indexes> { export async function proxyIndexOp(
const httpRes = await proxyRequest(url, 'index', search, data); url: string,
if (httpRes.status !== 200) { return [] } search: string,
data: AdditionalData
const rawIndexes = await httpRes.text(); ): Promise<Indexes> {
const httpRes = await proxyRequest(url, 'index', search, data)
if (httpRes.status !== 200) {
return []
}
const rawIndexes = await httpRes.text()
return parseIndexes(rawIndexes.split('\n')) return parseIndexes(rawIndexes.split('\n'))
} }

View File

@ -1,28 +1,34 @@
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import {
import { NestFactory } from '@nestjs/core'; FastifyAdapter,
import handlebars from 'hbs'; NestFastifyApplication,
} from '@nestjs/platform-fastify'
import { NestFactory } from '@nestjs/core'
import handlebars from 'hbs'
import { join } from 'path'; import { join } from 'path'
import { AppModule } from './app.module.js'; import { AppModule } from './app.module.js'
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter()); const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
)
const root = import.meta.dirname; const root = import.meta.dirname
app.useStaticAssets({ app.useStaticAssets({
root: join(root, '..', 'public'), root: join(root, '..', 'public'),
prefix: '/public/' prefix: '/public/',
}); })
app.setViewEngine({ app.setViewEngine({
engine: { engine: {
handlebars handlebars,
}, },
templates: join(root, '..', 'views') templates: join(root, '..', 'views'),
}) })
await app.listen(3000); await app.listen(3000)
} }
bootstrap(); bootstrap()

View File

@ -1,17 +1,14 @@
import { Controller, Get, Render } from "@nestjs/common"; import { Controller, Get, Render } from '@nestjs/common'
import { AllKeysProvider } from "./keys/providers/all.provider.js"; import { AllKeysProvider } from './keys/providers/all.provider.js'
@Controller() @Controller()
export class ViewsController { export class ViewsController {
constructor(private allKeysProvider: AllKeysProvider) {}
constructor(
private allKeysProvider: AllKeysProvider
) { }
@Get() @Get()
@Render('index.html') @Render('index.html')
index() { index() {
return { keys: this.allKeysProvider.getAll().map(x => x.url) } return { keys: this.allKeysProvider.getAll().map((x) => x.url) }
} }
} }

View File

@ -1,25 +1,25 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common'; import { INestApplication } from '@nestjs/common'
import request from 'supertest'; import request from 'supertest'
import { AppModule } from './../src/app.module.js'; import { AppModule } from './../src/app.module.js'
describe('AppController (e2e)', () => { describe('AppController (e2e)', () => {
let app: INestApplication; let app: INestApplication
beforeEach(async () => { beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({ const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule], imports: [AppModule],
}).compile(); }).compile()
app = moduleFixture.createNestApplication(); app = moduleFixture.createNestApplication()
await app.init(); await app.init()
}); })
it('/ (GET)', () => { it('/ (GET)', () => {
return request(app.getHttpServer()) return request(app.getHttpServer())
.get('/') .get('/')
.expect(200) .expect(200)
.expect((res) => res.headers['Content-Type'] == 'text/html'); .expect((res) => res.headers['Content-Type'] == 'text/html')
}); })
}); })