diff --git a/eslint.config.mjs b/eslint.config.mjs index a0a6ba1..43b6ba6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,5 +5,5 @@ export default [ { languageOptions: { parserOptions: { project: './tsconfig.eslint.json' } } }, ...typescript, { rules: { 'import/no-named-as-default-member': 'off' } }, - prettier + prettier, ]; diff --git a/package.json b/package.json index 2ac7f97..e0d338f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint": "eslint .", "lint:fix": "eslint . --fix", "format": "prettier --check .", - "format-fix": "prettier --write ." + "format:fix": "prettier --write ." }, "author": "The 39th board of Study Association GEWIS", "dependencies": { diff --git a/src/auth/LDAPStrategy.ts b/src/auth/LDAPStrategy.ts index 3cfe6fd..47e3dd2 100644 --- a/src/auth/LDAPStrategy.ts +++ b/src/auth/LDAPStrategy.ts @@ -9,6 +9,14 @@ import UserService from '../services/UserService'; import { Roles } from '../entity/enums/Roles'; import AppDataSource from '../database'; +export interface LDAPUser { + memberOfFlattened: string[]; + mail: string; + givenName: string; + sn: string; + sAMAccountName: string; +} + const isDefined = (i: string | undefined) => i !== undefined && i !== ''; export const ldapEnabled = () => @@ -28,7 +36,7 @@ export const LDAPStrategy = new Strategy({ }, }); -const checkAllowedRoles = async (ldapUser: any): Promise => { +const checkAllowedRoles = async (ldapUser: LDAPUser): Promise => { const roles = await AppDataSource.getRepository(Role).find(); const userRoles: Roles[] = []; roles.forEach((role) => { @@ -39,7 +47,7 @@ const checkAllowedRoles = async (ldapUser: any): Promise => { return userRoles; }; -export const updateUserInformation = async (user: User, ldapUser: any): Promise => { +export const updateUserInformation = async (user: User, ldapUser: LDAPUser): Promise => { const userRoles = await checkAllowedRoles(ldapUser); await new UserService().assignRoles(user, userRoles); @@ -54,7 +62,7 @@ export const updateUserInformation = async (user: User, ldapUser: any): Promise< }; export const ldapLogin = (req: express.Request, res: express.Response, next: express.NextFunction) => { - passport.authenticate('ldapauth', async (err: any, ldapUser: any, info: any) => { + passport.authenticate('ldapauth', async (err: Error, ldapUser: LDAPUser, info: any) => { if (err) { return next(err); } @@ -84,13 +92,13 @@ export const ldapLogin = (req: express.Request, res: express.Response, next: exp lastName: ldapUser.sn, email: ldapUser.mail, function: '', - } as any as User; + } as User; user = await userRepo.save(user); identity = { id: user.id, username: ldapUser.sAMAccountName, - } as any as IdentityLDAP; + } as IdentityLDAP; identity = await identityRepo.save(identity); identity = await identityRepo.findOne({ where: { id: identity.id }, diff --git a/src/dbvalidator/GEWISrecipient.ts b/src/dbvalidator/GEWISrecipient.ts index ecfa6f4..6b07012 100644 --- a/src/dbvalidator/GEWISrecipient.ts +++ b/src/dbvalidator/GEWISrecipient.ts @@ -36,10 +36,10 @@ export async function replaceGEWISRecipient() { '\\GEWISRecipient\\xspace', ); - p.save(); + p.save().catch((e) => console.error(e)); }); - console.log( + console.warn( `The following products had one or multiple instances of {instelling} replaced (${count}): ${logResult.substr(0, logResult.length - 2)}`, ); } diff --git a/src/dbvalidator/contracts.ts b/src/dbvalidator/contracts.ts index f4cc9bb..138e982 100644 --- a/src/dbvalidator/contracts.ts +++ b/src/dbvalidator/contracts.ts @@ -21,23 +21,25 @@ export async function allContractsAreCreated() { contracts.forEach((c) => { const createdStatus = c.activities.find((a) => a.subType === ContractStatus.CREATED); if (createdStatus === undefined) { - activityRepo.save({ - createdAt: new Date(c.createdAt.getDate() - 1), - updatedAt: new Date(), - createdById: c.createdById, - contractId: c.id, - type: ActivityType.STATUS, - subType: ContractStatus.CREATED, - descriptionEnglish: '', - descriptionDutch: '', - } as ContractActivity); + activityRepo + .save({ + createdAt: new Date(c.createdAt.getDate() - 1), + updatedAt: new Date(), + createdById: c.createdById, + contractId: c.id, + type: ActivityType.STATUS, + subType: ContractStatus.CREATED, + descriptionEnglish: '', + descriptionDutch: '', + } as ContractActivity) + .catch((e) => console.error(e)); logResult += `C${c.id}, `; count++; } }); - console.log( + console.warn( `The following contracts did not have a 'CREATED' status (${count}): ${logResult.substr(0, logResult.length - 2)}`, ); } @@ -61,16 +63,18 @@ export async function allProductsAreCancelledIfContractIsCancelled() { const index = p.activities.find((a) => a.subType === ProductInstanceStatus.CANCELLED); if (index === undefined) { - productInstanceActivityRepo.save({ - createdAt: cancelledActivity.createdAt, - updatedAt: new Date(), - productInstanceId: p.id, - createdById: c.createdById, - type: ActivityType.STATUS, - subType: ProductInstanceStatus.CANCELLED, - descriptionEnglish: '', - descriptionDutch: '', - } as ProductInstanceActivity); + productInstanceActivityRepo + .save({ + createdAt: cancelledActivity.createdAt, + updatedAt: new Date(), + productInstanceId: p.id, + createdById: c.createdById, + type: ActivityType.STATUS, + subType: ProductInstanceStatus.CANCELLED, + descriptionEnglish: '', + descriptionDutch: '', + } as ProductInstanceActivity) + .catch((e) => console.error(e)); logResult += `C${c.id} (P${p.id}), `; count++; @@ -79,7 +83,7 @@ export async function allProductsAreCancelledIfContractIsCancelled() { } }); - console.log( + console.warn( `The following cancelled contracts had non-cancelled products (${count}): ${logResult.substr(0, logResult.length - 2)}`, ); } @@ -105,16 +109,18 @@ export async function allProductsAreDeliveredIfContractIsFinished() { ); if (index === undefined) { - productInstanceActivityRepo.save({ - createdAt: finishedActivity.createdAt, - updatedAt: new Date(), - productInstanceId: p.id, - createdById: c.createdById, - type: ActivityType.STATUS, - subType: ProductInstanceStatus.DELIVERED, - descriptionEnglish: '', - descriptionDutch: '', - } as ProductInstanceActivity); + productInstanceActivityRepo + .save({ + createdAt: finishedActivity.createdAt, + updatedAt: new Date(), + productInstanceId: p.id, + createdById: c.createdById, + type: ActivityType.STATUS, + subType: ProductInstanceStatus.DELIVERED, + descriptionEnglish: '', + descriptionDutch: '', + } as ProductInstanceActivity) + .catch((e) => console.error(e)); logResult += `C${c.id} (P${p.id}), `; count++; @@ -123,7 +129,7 @@ export async function allProductsAreDeliveredIfContractIsFinished() { } }); - console.log( + console.warn( `The following contracts were finished, but did not have delivered/cancelled products (${count}): ${logResult.substr(0, logResult.length - 2)}`, ); } diff --git a/src/dbvalidator/invoices.ts b/src/dbvalidator/invoices.ts index 35bf5cc..2daf35e 100644 --- a/src/dbvalidator/invoices.ts +++ b/src/dbvalidator/invoices.ts @@ -17,23 +17,25 @@ export async function allInvoicesAreCreated() { invoices.forEach((i) => { const createdStatus = i.activities.find((a) => a.subType === InvoiceStatus.CREATED); if (createdStatus === undefined) { - activityRepo.save({ - createdAt: new Date(i.createdAt.getDate() - 1), - updatedAt: new Date(), - createdById: i.createdById, - invoiceId: i.id, - type: ActivityType.STATUS, - subType: InvoiceStatus.CREATED, - descriptionEnglish: '', - descriptionDutch: '', - } as InvoiceActivity); + activityRepo + .save({ + createdAt: new Date(i.createdAt.getDate() - 1), + updatedAt: new Date(), + createdById: i.createdById, + invoiceId: i.id, + type: ActivityType.STATUS, + subType: InvoiceStatus.CREATED, + descriptionEnglish: '', + descriptionDutch: '', + } as InvoiceActivity) + .catch((e) => console.error(e)); logResult += `F${i.id}, `; count++; } }); - console.log( + console.warn( `The following invoices did not have a 'CREATED' status (${count}): ${logResult.substr(0, logResult.length - 2)}`, ); } diff --git a/src/dbvalidator/productInstances.ts b/src/dbvalidator/productInstances.ts index 4968d22..3c03072 100644 --- a/src/dbvalidator/productInstances.ts +++ b/src/dbvalidator/productInstances.ts @@ -17,23 +17,25 @@ export async function allProductInstancesWereNotDelivered() { productInstances.forEach((p) => { const notDeliveredStatus = p.activities.find((a) => a.subType === ProductInstanceStatus.NOTDELIVERED); if (notDeliveredStatus === undefined) { - activityRepo.save({ - createdAt: new Date(p.createdAt.getDate() - 1), - updatedAt: new Date(), - productInstanceId: p.id, - createdById: p.contract.createdById, - type: ActivityType.STATUS, - subType: ProductInstanceStatus.NOTDELIVERED, - descriptionEnglish: '', - descriptionDutch: '', - } as ProductInstanceActivity); + activityRepo + .save({ + createdAt: new Date(p.createdAt.getDate() - 1), + updatedAt: new Date(), + productInstanceId: p.id, + createdById: p.contract.createdById, + type: ActivityType.STATUS, + subType: ProductInstanceStatus.NOTDELIVERED, + descriptionEnglish: '', + descriptionDutch: '', + } as ProductInstanceActivity) + .catch((e) => console.error(e)); logResult += `C${p.contractId} (P${p.id}), `; count++; } }); - console.log( + console.warn( `The following contracts had products that do not have a 'NOTDELIVERED' status (${count}): ${logResult.substr(0, logResult.length - 2)}`, ); } diff --git a/src/dbvalidator/validate.ts b/src/dbvalidator/validate.ts index efc668c..6d89f47 100644 --- a/src/dbvalidator/validate.ts +++ b/src/dbvalidator/validate.ts @@ -11,16 +11,18 @@ import { replaceGEWISRecipient } from './GEWISrecipient'; dotenv.config({ path: '.env' }); -AppDataSource.initialize().then(async () => { - const t1 = new Date(); - console.log('Start database validation...'); - await Promise.all([ - allContractsAreCreated(), - allInvoicesAreCreated(), - allProductInstancesWereNotDelivered(), - allProductsAreCancelledIfContractIsCancelled(), - allProductsAreDeliveredIfContractIsFinished(), - replaceGEWISRecipient(), - ]); - console.log(`Database validated in ${new Date().getTime() - t1.getTime()}ms`); -}); +AppDataSource.initialize() + .then(async () => { + const t1 = new Date(); + console.warn('Start database validation...'); + await Promise.all([ + allContractsAreCreated(), + allInvoicesAreCreated(), + allProductInstancesWereNotDelivered(), + allProductsAreCancelledIfContractIsCancelled(), + allProductsAreDeliveredIfContractIsFinished(), + replaceGEWISRecipient(), + ]); + console.warn(`Database validated in ${new Date().getTime() - t1.getTime()}ms`); + }) + .catch((e) => console.error(e)); diff --git a/src/helpers/activity.ts b/src/helpers/activity.ts index 9517c67..968d2fc 100644 --- a/src/helpers/activity.ts +++ b/src/helpers/activity.ts @@ -41,7 +41,7 @@ function printStringArrayToString(items: string[], language: Language, preSuffix conjunction = 'and'; break; default: - throw new TypeError(`Unknown language: ${language}`); + throw new TypeError(`Unknown language: ${language as string}`); } return items.reduce((result, s, i) => { @@ -84,18 +84,18 @@ async function parsePropertyChanges( if (keys.length === 0) return ''; // Before we continue, we need to process some relational attributes - const processedNew: any = {}; - const processedOld: any = {}; + const processedNew = {} as T; + const processedOld = {} as T; let newEnt; let oldEnt; await Promise.all( keys.map(async (k) => { // Parse all possible relational attributes ID's to their respective names + // TODO needs type assertions switch (k) { case 'productId': - // @ts-ignore newEnt = await AppDataSource.getRepository(Product).findOne(newProperties.productId); - // @ts-ignore + oldEnt = await AppDataSource.getRepository(Product).findOne(oldProperties.productId); switch (language) { case Language.DUTCH: @@ -113,42 +113,37 @@ async function parsePropertyChanges( processedOld.product = oldEnt != null ? oldEnt.nameEnglish : '...'; break; case 'companyId': - // @ts-ignore newEnt = await AppDataSource.getRepository(Company).findOne(newProperties.companyId); - // @ts-ignore + oldEnt = await AppDataSource.getRepository(Company).findOne(oldProperties.companyId); processedNew.company = newEnt != null ? newEnt.name : '...'; processedOld.company = oldEnt != null ? oldEnt.name : '...'; break; case 'contactId': - // @ts-ignore newEnt = await AppDataSource.getRepository(Contact).findOne(newProperties.contactId); - // @ts-ignore + oldEnt = await AppDataSource.getRepository(Contact).findOne(oldProperties.contactId); processedNew.contact = newEnt != null ? newEnt.fullName() : '...'; processedOld.contact = oldEnt != null ? oldEnt.fullName() : '...'; break; case 'assignedToId': - // @ts-ignore newEnt = await AppDataSource.getRepository(User).findOne(newProperties.assignedToId); - // @ts-ignore + oldEnt = await AppDataSource.getRepository(User).findOne(oldProperties.assignedToId); processedNew.assignment = newEnt != null ? newEnt.fullName() : '...'; processedOld.assignment = oldEnt != null ? oldEnt.fullName() : '...'; break; case 'categoryId': - // @ts-ignore newEnt = await AppDataSource.getRepository(ProductCategory).findOne(newProperties.categoryId); - // @ts-ignore + oldEnt = await AppDataSource.getRepository(ProductCategory).findOne(oldProperties.categoryId); processedNew.category = newEnt != null ? newEnt.name : '...'; processedOld.category = oldEnt != null ? oldEnt.name : '...'; break; // If it is not a relational attribute, simply copy the value with the same key default: - // @ts-ignore processedNew[k] = newProperties[k]; - // @ts-ignore + processedOld[k] = oldProperties[k]; } }), @@ -291,39 +286,36 @@ export async function createActivitiesForEntityEdits( return false; } - // @ts-ignore await repo.update(entity.id, params); // If the assigned user has changed, we create an activity for this. if (Object.keys(changes).includes('assignedToId')) { - // @ts-ignore await activityService.createActivity(ActivityEntity, { descriptionDutch: createReassignActivityDescription( - // @ts-ignore As checked in the if-statement above, the "changes" variable does have + // As checked in the if-statement above, the "changes" variable does have // an assignedToId value await new UserService().getUser(changes.assignedToId), - // @ts-ignore Therefore, the real entity must also have this property by definition + // Therefore, the real entity must also have this property by definition await new UserService().getUser(entity.assignedToId), Language.DUTCH, ), descriptionEnglish: createReassignActivityDescription( - // @ts-ignore As checked in the if-statement above, the "changes" variable does have + // As checked in the if-statement above, the "changes" variable does have // an assignedToId value await new UserService().getUser(changes.assignedToId), - // @ts-ignore Therefore, the real entity must also have this property by definition + // Therefore, the real entity must also have this property by definition await new UserService().getUser(entity.assignedToId), Language.ENGLISH, ), entityId: entity.id, type: ActivityType.REASSIGN, }); - // @ts-ignore Remove this change, because we have created an activity for it. + // Remove this change, because we have created an activity for it. delete changes.assignedToId; } // If any other properties have changed, we create an "EDIT" activity for this. if (Object.keys(changes).length > 0) { - // @ts-ignore await activityService.createActivity(ActivityEntity, { descriptionDutch: await createEditActivityDescription(changes, entity, Language.DUTCH), descriptionEnglish: await createEditActivityDescription(changes, entity, Language.ENGLISH), diff --git a/src/helpers/currency.ts b/src/helpers/currency.ts index 9467ee0..db23da6 100644 --- a/src/helpers/currency.ts +++ b/src/helpers/currency.ts @@ -11,7 +11,7 @@ export default class Currency { locale = 'nl-NL'; break; default: - throw new TypeError(`Unknown language: ${language}`); + throw new TypeError(`Unknown language: ${language as string}`); } return new Intl.NumberFormat(locale, { maximumFractionDigits: 2, minimumFractionDigits: 2 }).format(price / 100); diff --git a/src/helpers/entityChanges.ts b/src/helpers/entityChanges.ts index 9db2ac3..3543311 100644 --- a/src/helpers/entityChanges.ts +++ b/src/helpers/entityChanges.ts @@ -6,12 +6,16 @@ export default function getEntityChanges(newEntity: Partial, oldEntity: T): Partial { const result: Partial = {}; - Object.keys(newEntity).forEach((k) => { - // @ts-ignore - if (!(newEntity[k] instanceof Date && newEntity[k].getTime() === oldEntity[k].getTime())) { - // @ts-ignore + Object.keys(newEntity).forEach((key: string) => { + const k = key as keyof T; + if ( + !( + newEntity[k] instanceof Date && + oldEntity[k] instanceof Date && + newEntity[k].getTime() === oldEntity[k].getTime() + ) + ) { if (newEntity[k] !== oldEntity[k]) { - // @ts-ignore result[k] = newEntity[k]; } } diff --git a/src/helpers/fileHelper.ts b/src/helpers/fileHelper.ts index b6b6f00..c2650b7 100644 --- a/src/helpers/fileHelper.ts +++ b/src/helpers/fileHelper.ts @@ -57,8 +57,8 @@ export default class FileHelper { public static removeFile(file: BaseFile) { try { fs.unlinkSync(file.location); - } catch (e) { - console.log(`File ${file.name} at ${file.location} does not exist, so could not be removed`); + } catch { + console.warn(`File ${file.name} at ${file.location} does not exist, so could not be removed`); } } @@ -69,8 +69,8 @@ export default class FileHelper { public static removeFileAtLoc(location: string) { try { fs.unlinkSync(location); - } catch (e) { - console.log(`File ${location} does not exist, so could not be removed`); + } catch { + console.warn(`File ${location} does not exist, so could not be removed`); } } } diff --git a/src/helpers/filters.ts b/src/helpers/filters.ts index a1bbb62..eccec38 100644 --- a/src/helpers/filters.ts +++ b/src/helpers/filters.ts @@ -17,7 +17,6 @@ export function addQueryFilters(filters?: ListOrFilter[]) { if (filters !== undefined && filters && filters.length > 0) { const result: FindOptionsWhere = {}; filters.forEach((f) => { - // @ts-ignore result[f.column] = f.values.length !== 1 ? In(f.values) : f.values[0]; }); return result; @@ -45,7 +44,6 @@ export function addQuerySearch(fieldNames: string[], search?: temp2[intermediates[i]] = temp; temp = temp2; } - console.log(temp); return temp; }), ); diff --git a/src/index.ts b/src/index.ts index d8d4e38..d42ec99 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,17 @@ -/* eslint import/first: off */ import 'reflect-metadata'; import * as fs from 'fs'; +import path from 'path'; import express, { Express } from 'express'; import dotenv from 'dotenv'; -dotenv.config({ path: '.env' }); - import errorhandler from 'strong-error-handler'; import swaggerUi from 'swagger-ui-express'; -import path from 'path'; import methodOverride from 'method-override'; import bodyParser from 'body-parser'; import session from 'express-session'; import { TypeormStore } from 'connect-typeorm'; import passport from 'passport'; +import { DataSource } from 'typeorm'; import swaggerDocument from '../build/swagger.json'; import { RegisterRoutes } from '../build/routes'; import startEvents from './timedevents/cron'; @@ -32,7 +30,8 @@ import { User } from './entity/User'; import UserService from './services/UserService'; import { ldapLogin, LDAPStrategy } from './auth'; import AppDataSource from './database'; -import { DataSource } from 'typeorm'; + +dotenv.config({ path: '.env' }); const PORT = process.env.PORT || 3001; @@ -61,89 +60,88 @@ export function setupSessionSupport(dataSource: DataSource, app: Express) { app.use(passport.initialize()); app.use(passport.session()); - - // Initialize passport config. - // config(); } -AppDataSource.initialize().then(async (dataSource) => { - // Setup of database - await new UserService().setupRoles(); +AppDataSource.initialize() + .then(async (dataSource) => { + // Setup of database + await new UserService().setupRoles(); - const app = express(); + const app = express(); - app.use(express.json({ limit: '50mb' })); - app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' })); + app.use(express.json({ limit: '50mb' })); + app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' })); - app.set('trust proxy', 2); + app.set('trust proxy', 2); - setupSessionSupport(dataSource, app); + setupSessionSupport(dataSource, app); - passport.serializeUser((user: any, done) => { - done(null, user.id); - }); + passport.serializeUser((user: any, done) => { + done(null, user.id); + }); - passport.deserializeUser(async (id: number, done) => { - const userRepo = dataSource.getRepository(User); - const user = await userRepo.findOne({ where: { id }, relations: ['roles'] }); - if (user === undefined) { - return done(null, false); - } - return done(null, user); - }); + passport.deserializeUser(async (id: number, done) => { + const userRepo = dataSource.getRepository(User); + const user = await userRepo.findOne({ where: { id }, relations: ['roles'] }); + if (user === undefined) { + return done(null, false); + } + return done(null, user); + }); - passport.use(LDAPStrategy); - passport.use(localStrategy); + passport.use(LDAPStrategy); + passport.use(localStrategy); - app.post('/api/login/ldap', ldapLogin); - app.post('/api/login/local', localLogin); + app.post('/api/login/ldap', ldapLogin); + app.post('/api/login/local', localLogin); - RegisterRoutes(app); + RegisterRoutes(app); - app.use(methodOverride()); + app.use(methodOverride()); - // Create file generation folders - if (!fs.existsSync(path.join(__dirname, '/../tmp'))) { - fs.mkdirSync(path.join(__dirname, '/../tmp')); - } - if (!fs.existsSync(path.join(__dirname, '/../data/generated'))) { - // Recursive so data is also created - fs.mkdirSync(path.join(__dirname, '/../data/generated'), { recursive: true }); - } - if (!fs.existsSync(path.join(__dirname, '/../data/uploads'))) { - fs.mkdirSync(path.join(__dirname, '/../data/uploads')); - } - if (!fs.existsSync(path.join(__dirname, '/../data/logos'))) { - fs.mkdirSync(path.join(__dirname, '/../data/logos')); - } - if (!fs.existsSync(path.join(__dirname, '/../data/backgrounds'))) { - fs.mkdirSync(path.join(__dirname, '/../data/backgrounds')); - } + // Create file generation folders + if (!fs.existsSync(path.join(__dirname, '/../tmp'))) { + fs.mkdirSync(path.join(__dirname, '/../tmp')); + } + if (!fs.existsSync(path.join(__dirname, '/../data/generated'))) { + // Recursive so data is also created + fs.mkdirSync(path.join(__dirname, '/../data/generated'), { recursive: true }); + } + if (!fs.existsSync(path.join(__dirname, '/../data/uploads'))) { + fs.mkdirSync(path.join(__dirname, '/../data/uploads')); + } + if (!fs.existsSync(path.join(__dirname, '/../data/logos'))) { + fs.mkdirSync(path.join(__dirname, '/../data/logos')); + } + if (!fs.existsSync(path.join(__dirname, '/../data/backgrounds'))) { + fs.mkdirSync(path.join(__dirname, '/../data/backgrounds')); + } - // Give additional error information when in development mode. - app.use( - errorhandler({ - debug: process.env.NODE_ENV === 'development', - safeFields: ['message'], - }), - ); - - // If env file specifies development, use swagger UI - if (process.env.NODE_ENV === 'development') { - app.use('/api/swagger-ui', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); - app.get('/api/swagger.json', (req, res) => { - res.sendFile(path.join(__dirname, './public/swagger.json')); - }); - } + // Give additional error information when in development mode. + app.use( + errorhandler({ + debug: process.env.NODE_ENV === 'development', + safeFields: ['message'], + }), + ); + + // If env file specifies development, use swagger UI + if (process.env.NODE_ENV === 'development') { + app.use('/api/swagger-ui', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); + app.get('/api/swagger.json', (req, res) => { + res.sendFile(path.join(__dirname, './public/swagger.json')); + }); + } - app.use('/static/logos', express.static(path.join(__dirname, '../data/logos'))); - app.use('/static/backgrounds', express.static(path.join(__dirname, '../data/backgrounds'))); + app.use('/static/logos', express.static(path.join(__dirname, '../data/logos'))); + app.use('/static/backgrounds', express.static(path.join(__dirname, '../data/backgrounds'))); - // Announce port that is listened to in the console - app.listen(PORT, () => { - console.log(`Server is listening on port ${PORT}`); - }); + // Announce port that is listened to in the console + app.listen(PORT, () => { + console.warn(`Server is listening on port ${PORT}`); + }); - // Enable timed events - startEvents(); -}); + // Enable timed events + startEvents(); + }) + .catch((e) => console.error(e)); diff --git a/src/mailer/Mailer.ts b/src/mailer/Mailer.ts index 5ef5b2c..c6ff7fe 100644 --- a/src/mailer/Mailer.ts +++ b/src/mailer/Mailer.ts @@ -23,9 +23,9 @@ export class Mailer { async send(mail: Mail.Options) { try { const info = await this.transporter.sendMail(mail); - console.log('Message sent: %s', info.messageId); + console.warn('Message sent: %s', info.messageId); } catch (e) { - console.log(e); + console.warn(e); } } } diff --git a/src/services/ActivityService.ts b/src/services/ActivityService.ts index 39db3af..76e3600 100644 --- a/src/services/ActivityService.ts +++ b/src/services/ActivityService.ts @@ -27,7 +27,7 @@ export interface ActivityParams { export interface FullActivityParams { entityId: number; type: ActivityType; - subType?: any; + subType?: ContractStatus; descriptionEnglish: string; descriptionDutch: string; } @@ -44,16 +44,16 @@ export interface ProductInstanceStatusParams extends ActivityParams { subType: ProductInstanceStatus; } -export default class ActivityService { +export default class ActivityService { repo: Repository; /** Child class of BaseActivity */ - EntityActivity: T; + EntityActivity: Activity; /** Represents the logged in user, performing an operation */ actor?: User; - constructor(EntityActivity: T, options?: { actor?: User }) { + constructor(EntityActivity: Activity, options?: { actor?: User }) { this.EntityActivity = EntityActivity; this.repo = AppDataSource.getRepository(EntityActivity.constructor.name); this.actor = options?.actor; @@ -64,7 +64,7 @@ export default class ActivityService { * @param activity Activity object * @param entityId ID of an entity (e.g. contract, invoice, company, etc) */ - validateActivity(activity: T, entityId: number): any { + validateActivity(activity: Activity, entityId: number): Activity { if (activity === undefined) { throw new ApiError(HTTPStatus.NotFound, 'Activity not found'); } @@ -75,19 +75,18 @@ export default class ActivityService { return activity; } - async getActivity(id: number, relations: string[] = []): Promise { + async getActivity(id: number, relations: string[] = []): Promise { const activity = await this.repo.findOne({ where: { id }, relations }); if (activity == null) { throw new ApiError(HTTPStatus.NotFound, `An activity with ID ${id} cannot be found`); } - return activity as T; + return activity as Activity; } /** * Get the current status-enum belong to the given entity */ async getCurrentStatus(entity: object): Promise { - // @ts-ignore let activities = await this.repo.find({ where: { ...entity, @@ -96,7 +95,7 @@ export default class ActivityService { }); activities = activities.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); - // @ts-ignore + return activities[0].subType; } @@ -105,7 +104,6 @@ export default class ActivityService { * @param entity Object containing the Many-To-One column to search on, e.g. { contractId: 1 } */ async getStatuses(entity: object): Promise> { - // @ts-ignore const activities = await this.repo.find({ select: ['subType'] as any, where: { @@ -114,7 +112,6 @@ export default class ActivityService { }, }); - // @ts-ignore return activities.map((activity) => activity.subType); } @@ -231,7 +228,7 @@ export default class ActivityService { case 'InvoiceActivity': activity = act; if (activity.subType === InvoiceStatus.SENT) { - sendInvoiceEmails(activity.invoiceId).then(() => {}); + await sendInvoiceEmails(activity.invoiceId).then(() => {}); } statuses = await this.getStatuses({ invoiceId: activity.invoiceId }); @@ -289,8 +286,7 @@ export default class ActivityService { * @param params Parameters to create an activity with */ - async createActivity(C: { new (): T }, params: FullActivityParams): Promise { - // @ts-ignore + async createActivity(C: { new (): Activity }, params: FullActivityParams): Promise { let activity = new C(); activity.setRelatedEntityId(params.entityId); activity.descriptionDutch = params.descriptionDutch; @@ -367,7 +363,7 @@ export default class ActivityService { where: { contractId, createdById: this.actor?.id, - } as any, + }, order: { updatedAt: 'DESC', }, @@ -396,7 +392,7 @@ export default class ActivityService { }); } else { // Add a new Product activity - // @ts-ignore + await this.createActivity(ContractActivity, { descriptionDutch: createAddProductActivityDescription([productName], Language.DUTCH), descriptionEnglish: createAddProductActivityDescription([productName], Language.ENGLISH), @@ -412,8 +408,8 @@ export default class ActivityService { * @param activityId ID of the activity * @param params Subset of update parameters */ - async updateActivity(entityId: number, activityId: number, params: Partial): Promise { - let activity = (await this.repo.findOneBy({ id: activityId })) as T; + async updateActivity(entityId: number, activityId: number, params: Partial): Promise { + let activity = (await this.repo.findOneBy({ id: activityId })) as Activity; if (activity == null) throw new ApiError(HTTPStatus.NotFound); activity = this.validateActivity(activity, entityId); const p = { @@ -422,7 +418,7 @@ export default class ActivityService { }; await this.repo.update(activity.id, p); - activity = (await this.repo.findOneBy({ id: activityId })) as T; + activity = (await this.repo.findOneBy({ id: activityId })) as Activity; return activity; } @@ -436,20 +432,18 @@ export default class ActivityService { throw new ApiError(HTTPStatus.BadRequest, 'Cannot delete activities from invoices'); } - let activity = (await this.repo.findOneBy({ id: activityId })) as T; + let activity = (await this.repo.findOneBy({ id: activityId })) as Activity; if (activity == null) throw new ApiError(HTTPStatus.NotFound); activity = this.validateActivity(activity, entityId); if (activity === undefined) { return; } - // @ts-ignore + if ( activity.type === ActivityType.STATUS && (activity.subType === ContractStatus.CREATED || - // @ts-ignore activity.subType === InvoiceStatus.CREATED || - // @ts-ignore activity.subType === ProductInstanceStatus.NOTDELIVERED) ) { throw new ApiError(HTTPStatus.BadRequest, 'Cannot delete the initial (created) status of an entity'); diff --git a/src/services/AuthService.ts b/src/services/AuthService.ts index c3284ac..f25f055 100644 --- a/src/services/AuthService.ts +++ b/src/services/AuthService.ts @@ -49,7 +49,7 @@ export default class AuthService { this.userRepo = userRepo ?? AppDataSource.getRepository(User); } - async getAuthStatus(req: express.Request): Promise { + getAuthStatus(req: express.Request): AuthStatus { const authenticated = req.isAuthenticated(); return { @@ -76,7 +76,7 @@ export default class AuthService { async logout(req: express.Request): Promise { return new Promise((resolve, reject) => { - req.logout((error) => { + req.logout((error: Error) => { if (error) reject(error); resolve(); }); @@ -85,7 +85,7 @@ export default class AuthService { login(user: User, req: express.Request) { return new Promise((resolve, reject) => { - req.logIn(user, (err) => { + req.logIn(user, (err: Error) => { if (err) { return reject(err); } @@ -106,7 +106,7 @@ export default class AuthService { return; } - Mailer.getInstance().send( + await Mailer.getInstance().send( resetPassword( user, `${process.env.SERVER_HOST}/reset-password?token=${this.getResetPasswordToken(user, identity)}`, @@ -128,7 +128,7 @@ export default class AuthService { identity = (await this.identityLocalRepo.findOneBy({ id: user.id }))!; if (!silent) { - Mailer.getInstance().send( + await Mailer.getInstance().send( newUser(user, `${process.env.SERVER_HOST}/reset-password?token=${this.getSetPasswordToken(user, identity)}`), ); } @@ -173,7 +173,7 @@ export default class AuthService { user_id: user.id, }, // Password salt + user createdAt as unique key - `${identity.salt || ''}.${user.createdAt}`, + `${identity.salt || ''}.${user.createdAt.toString()}`, { expiresIn: '7 days' }, ); } @@ -185,7 +185,7 @@ export default class AuthService { user_id: user.id, }, // Password salt + user createdAt as unique key - `${identity.salt || ''}.${user.createdAt}`, + `${identity.salt || ''}.${user.createdAt.toString()}`, { expiresIn: '7 days' }, ); } @@ -219,10 +219,8 @@ export default class AuthService { }); return; } - default: - throw new ApiError(HTTPStatus.BadRequest, INVALID_TOKEN); } - } catch (e) { + } catch { throw new ApiError(HTTPStatus.BadRequest, INVALID_TOKEN); } } @@ -240,7 +238,7 @@ export default class AuthService { throw new ApiError(HTTPStatus.BadRequest, "You don't have an API key yet."); } - Mailer.getInstance().send(viewApiKey(user, `${process.env.SERVER_HOST}/`)); + await Mailer.getInstance().send(viewApiKey(user, `${process.env.SERVER_HOST}/`)); return identity.apiKey; } @@ -261,7 +259,7 @@ export default class AuthService { await this.identityApiKeyRepo.insert(identity); - Mailer.getInstance().send(newApiKey(user, `${process.env.SERVER_HOST}/`)); + await Mailer.getInstance().send(newApiKey(user, `${process.env.SERVER_HOST}/`)); return identity.apiKey; } diff --git a/src/services/CompanyService.ts b/src/services/CompanyService.ts index b05d81f..1afe2d0 100644 --- a/src/services/CompanyService.ts +++ b/src/services/CompanyService.ts @@ -98,7 +98,7 @@ export default class CompanyService { createCompany(params: CompanyParams): Promise { const company = { ...params, - } as any as Company; + } as Company; return this.repo.save(company); } diff --git a/src/services/ContactService.ts b/src/services/ContactService.ts index 978341e..a5d77d4 100644 --- a/src/services/ContactService.ts +++ b/src/services/ContactService.ts @@ -83,7 +83,7 @@ export default class ContactService { async createContact(params: ContactParams): Promise { const contact = { ...params, - } as any as Contact; + } as Contact; return this.repo.save(contact); } diff --git a/src/services/FileService.ts b/src/services/FileService.ts index af40c47..25820ed 100644 --- a/src/services/FileService.ts +++ b/src/services/FileService.ts @@ -202,7 +202,6 @@ export default class FileService { private static handleFile(request: express.Request): Promise { const multerSingle = multer().single('file'); return new Promise((resolve, reject) => { - // @ts-ignore multerSingle(request, undefined, async (error: Error) => { if (error) { reject(error); @@ -249,7 +248,6 @@ export default class FileService { } async createFileObject(params: FullFileParams) { - // @ts-ignore let file = new this.EntityFile(); file = { ...file, diff --git a/src/services/ProductCategoryService.ts b/src/services/ProductCategoryService.ts index 9406b4a..fa3a67c 100644 --- a/src/services/ProductCategoryService.ts +++ b/src/services/ProductCategoryService.ts @@ -60,7 +60,7 @@ export default class ProductCategoryService { async createCategory(params: CategoryParams): Promise { const category = { ...params, - } as any as ProductCategory; + } as ProductCategory; return this.repo.save(category); } diff --git a/src/services/ProductInstanceService.ts b/src/services/ProductInstanceService.ts index aa4f846..8995791 100644 --- a/src/services/ProductInstanceService.ts +++ b/src/services/ProductInstanceService.ts @@ -60,7 +60,7 @@ export default class ProductInstanceService { let productInstance = { ...params, contractId, - } as any as ProductInstance; + } as ProductInstance; if (product.status === ProductStatus.INACTIVE) { throw new ApiError(HTTPStatus.BadRequest, 'Cannot add inactive products to contracts'); diff --git a/src/services/StatisticsService.ts b/src/services/StatisticsService.ts index af0f5b8..b042e01 100644 --- a/src/services/StatisticsService.ts +++ b/src/services/StatisticsService.ts @@ -46,8 +46,7 @@ export default class StatisticsService { .getOne(); let start: Date; if (startYear) { - // @ts-ignore - start = startYear.createdAt; + start = startYear.createdAt as Date; } else { start = new Date(); } @@ -189,7 +188,7 @@ export default class StatisticsService { amount: 0, nrOfProducts: 0, year: result[0].year + i, - } as any as AnalysisResultByYear); + } as AnalysisResultByYear); } } diff --git a/src/services/UserService.ts b/src/services/UserService.ts index b28317e..e0c9786 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -152,7 +152,7 @@ export default class UserService { if (ldapEnabled()) throw new ApiError(HTTPStatus.BadRequest, 'Cannot create a local user, because LDAP authentication is enabled'); - const { roles, ldapOverrideEmail, ...userParams } = params; + const { roles, ...userParams } = params; let user = this.repo.create(userParams); user = await this.repo.save(user); if (roles) { diff --git a/src/timedevents/cron.ts b/src/timedevents/cron.ts index f9cdeea..b1fb8e3 100644 --- a/src/timedevents/cron.ts +++ b/src/timedevents/cron.ts @@ -7,24 +7,24 @@ import ldapGroups from './events/ldapGroups'; export default function startEvents() { // On July 1st every year, remove the "Deferred" status from all products - cron.schedule('0 0 0 1 7 *', async () => { - await deferredProducts(); + cron.schedule('0 0 0 1 7 *', () => { + deferredProducts().catch((e) => console.error(e)); }); // Every night at 4:30, the "tmp" folder is deleted and recreated to preserve disk space - cron.schedule('0 29 3 * * *', async () => { - await tmpFolder(); + cron.schedule('0 29 3 * * *', () => { + tmpFolder().catch((e) => console.error(e)); }); // Every night at 4:30, fetch the current DirectMail information and process it - cron.schedule('0 29 3 * * *', async () => { - await directMail(); + cron.schedule('0 29 3 * * *', () => { + directMail().catch((e) => console.error(e)); }); // Every night at 4:30, update all user roles from LDAP if LDAP is enabled - cron.schedule('0 29 3 * * *', async () => { - if (ldapEnabled()) await ldapGroups(); + cron.schedule('0 29 3 * * *', () => { + if (ldapEnabled()) ldapGroups().catch((e) => console.error(e)); }); - console.log('Scheduled timed events'); + console.warn('Scheduled timed events'); } diff --git a/src/timedevents/events/deferredProducts.ts b/src/timedevents/events/deferredProducts.ts index 32d0c01..2d0ed6d 100644 --- a/src/timedevents/events/deferredProducts.ts +++ b/src/timedevents/events/deferredProducts.ts @@ -2,7 +2,7 @@ import ProductInstanceService from '../../services/ProductInstanceService'; export default async function deferredProducts() { await new ProductInstanceService().removeDeferredStatuses(); - console.log('All "deferred" statuses have been removed!'); - console.log('Good luck to the new board!!!'); - console.log('~ The 39th board of GEWIS'); + console.warn('All "deferred" statuses have been removed!'); + console.warn('Good luck to the new board!!!'); + console.warn('~ The 39th board of GEWIS'); } diff --git a/src/timedevents/events/directMail.ts b/src/timedevents/events/directMail.ts index d023d4e..d7bc417 100644 --- a/src/timedevents/events/directMail.ts +++ b/src/timedevents/events/directMail.ts @@ -1,6 +1,12 @@ import axios from 'axios'; import ProductService from '../../services/ProductService'; +export interface DirectMailResponse { + description: string; + eMail: string; + noMembers: string; +} + export default async function directMail() { if ( !process.env.DIRECTMAIL_URL || @@ -20,12 +26,16 @@ export default async function directMail() { }) .then(async (response) => { const header = [['Name', 'Email', 'Students']]; - const parsedData: string[][] = response.data.map((d: any) => [d.description, d.eMail, d.noMembers]); + const parsedData: string[][] = (response.data as DirectMailResponse[]).map((d: DirectMailResponse) => [ + d.description, + d.eMail, + d.noMembers, + ]); await new ProductService().updatePricing(parseInt(process.env.DIRECTMAIL_PRODUCT_ID!, 10), { data: header.concat(parsedData), }); }) - .catch((error) => { - console.error(error); + .catch((err) => { + console.error(err); }); } diff --git a/src/timedevents/events/ldapGroups.ts b/src/timedevents/events/ldapGroups.ts index 9578eea..1b72ecd 100644 --- a/src/timedevents/events/ldapGroups.ts +++ b/src/timedevents/events/ldapGroups.ts @@ -1,7 +1,4 @@ -// The @types package from ldapjs is not installed, because it causes -// a conflict in TSOA with passport-ldapauth @types -// @ts-ignore -import { createClient } from 'ldapjs'; +import { createClient, SearchCallbackResponse, SearchEntry } from 'ldapjs'; import { IdentityLDAP } from '../../entity/IdentityLDAP'; import { updateUserInformation } from '../../auth'; import AppDataSource from '../../database'; @@ -24,13 +21,15 @@ export default async function ldapGroups() { scope: 'one', filter: (process.env.LDAP_SEARCHFILTER || '').replace('{{username}}', identity.username), }, - (err: any, res: any) => { - res.on('searchEntry', async (entry: any) => { - await updateUserInformation(identity.user, entry.object); + (err: Error | null, res: SearchCallbackResponse) => { + res.on('searchEntry', (entry: SearchEntry) => { + updateUserInformation(identity.user, entry.object).catch((err) => { + console.error(err); + }); }); }, ); }); - console.log('Updated user roles based on LDAP'); + console.error('Updated user roles based on LDAP'); } diff --git a/src/timedevents/events/ldapRoles.ts b/src/timedevents/events/ldapRoles.ts index 836a5c2..1a0510e 100644 --- a/src/timedevents/events/ldapRoles.ts +++ b/src/timedevents/events/ldapRoles.ts @@ -1,9 +1,12 @@ -// @ts-ignore -import { createClient } from 'ldapjs'; +import { createClient, LDAPResult, SearchCallbackResponse, SearchEntry, SearchRequest } from 'ldapjs'; import dotenv from 'dotenv'; import { ldapEnabled } from '../../auth'; import AuthService from '../../services/AuthService'; +export interface SearchReference { + uris: string[]; +} + export default async function ldapRoles() { dotenv.config(); @@ -23,30 +26,32 @@ export default async function ldapRoles() { { filter: process.env.LDAP_SEARCHFILTER!.replace('username', identity.username), }, - (error: any, res: any) => { + (error: Error | null, res: SearchCallbackResponse) => { if (error) { console.error(error); return; } - res.on('searchRequest', (searchRequest: any) => { - console.log('searchRequest: ', searchRequest); + res.on('searchRequest', (searchRequest: SearchRequest) => { + console.warn('searchRequest: ', searchRequest); }); - res.on('searchEntry', (entry: any) => { - console.log(`entry: ${JSON.stringify(entry.object)}`); + res.on('searchEntry', (entry: SearchEntry) => { + console.warn(`entry: ${JSON.stringify(entry.object)}`); }); - res.on('searchReference', (referral: any) => { - console.log(`referral: ${referral.uris.join()}`); + res.on('searchReference', (referral: SearchReference) => { + console.warn(`referral: ${referral.uris.join()}`); }); - res.on('error', (err: any) => { - console.error(`error: ${err.message}`); + res.on('error', (err: Error) => { + console.warn(`error: ${err.message}`); }); - res.on('end', (result: any) => { - console.log(`status: ${result}`); + res.on('end', (result: LDAPResult | null) => { + console.warn(`status: ${JSON.stringify(result)}`); }); }, ); }); } -ldapRoles().then(() => Promise.resolve()); +ldapRoles() + .then(() => Promise.resolve()) + .catch((e) => console.error(e)); diff --git a/src/timedevents/events/tmpFolder.ts b/src/timedevents/events/tmpFolder.ts index aa5ddee..52e54e1 100644 --- a/src/timedevents/events/tmpFolder.ts +++ b/src/timedevents/events/tmpFolder.ts @@ -3,9 +3,9 @@ import path from 'path'; import { rimraf } from 'rimraf'; export default async function tmpFolder() { - console.log('Remove temp folder...'); + console.warn('Remove temp folder...'); await rimraf(path.join(__dirname, '../../../tmp')); - console.log('Folder deleted'); + console.warn('Folder deleted'); fs.mkdirSync(path.join(__dirname, '../../../tmp')); - console.log('Removed and recreated a new "tmp" folder for temporary files'); + console.warn('Removed and recreated a new "tmp" folder for temporary files'); }