diff --git a/modules/authentication/src/Authentication.ts b/modules/authentication/src/Authentication.ts index e34dafdd9..8a881d1ba 100644 --- a/modules/authentication/src/Authentication.ts +++ b/modules/authentication/src/Authentication.ts @@ -676,11 +676,12 @@ export default class Authentication extends ManagedModule { const { email, teamId } = call.request; try { - const deletedToken = await Token.getInstance().deleteOne({ - // @ts-expect-error Unsafe nested property access + const invitationQuery: Indexable = { 'data.teamId': teamId, 'data.email': email, - }); + }; + + const deletedToken = await Token.getInstance().deleteOne(invitationQuery); if (deletedToken.deletedCount === 0) { return callback({ diff --git a/modules/authentication/src/handlers/metamask.ts b/modules/authentication/src/handlers/metamask.ts index c61394404..025e187ea 100644 --- a/modules/authentication/src/handlers/metamask.ts +++ b/modules/authentication/src/handlers/metamask.ts @@ -5,6 +5,7 @@ import { ConduitRouteActions, ConduitRouteReturnDefinition, GrpcError, + Indexable, ParsedRouterRequest, UnparsedRouterResponse, } from '@conduitplatform/grpc-sdk'; @@ -70,10 +71,11 @@ export class MetamaskHandlers implements IAuthenticationStrategy { const { ethPublicAddress } = call.request.params; const normalizedEthPublicAddress = ethPublicAddress.toLowerCase(); - const existingUser: User | null = await User.getInstance().findOne({ - // @ts-expect-error Unsafe nested property access + const metamaskQuery: Indexable = { 'metamask.ethPublicAddress': normalizedEthPublicAddress, - }); + }; + + const existingUser: User | null = await User.getInstance().findOne(metamaskQuery); if (existingUser) { return { nonce: existingUser.metamask!.nonce }; @@ -100,10 +102,11 @@ export class MetamaskHandlers implements IAuthenticationStrategy { throw new GrpcError(status.UNAUTHENTICATED, 'No headers provided'); } - const user = await User.getInstance().findOne({ - // @ts-expect-error Unsafe nested property access + const metamaskQuery: Indexable = { 'metamask.ethPublicAddress': normalizedEthPublicAddress, - }); + }; + + const user = await User.getInstance().findOne(metamaskQuery); if (isNil(user)) { throw new GrpcError( @@ -145,10 +148,11 @@ export class MetamaskHandlers implements IAuthenticationStrategy { ); } - await User.getInstance().findByIdAndUpdate(user._id, { - // @ts-expect-error Unsafe nested property access + const nonceUpdate: Indexable = { 'metamask.nonce': uuid(), - }); + }; + + await User.getInstance().findByIdAndUpdate(user._id, nonceUpdate); ConduitGrpcSdk.Metrics?.increment('logged_in_users_total'); const config = ConfigController.getInstance().config; diff --git a/modules/authentication/src/handlers/team.ts b/modules/authentication/src/handlers/team.ts index 63a9455bc..6fa2c2710 100644 --- a/modules/authentication/src/handlers/team.ts +++ b/modules/authentication/src/handlers/team.ts @@ -134,11 +134,12 @@ export class TeamsHandler implements IAuthenticationStrategy { } async getUserInvites(call: ParsedRouterRequest): Promise { - const invites = await Token.getInstance().findMany({ + const userInvitesQuery: Indexable = { tokenType: TokenType.TEAM_INVITE_TOKEN, - // @ts-ignore 'data.email': call.request.context.user.email, - }); + }; + + const invites = await Token.getInstance().findMany(userInvitesQuery); return { invites: invites.map(invite => ({ teamId: invite.data.teamId, @@ -579,12 +580,13 @@ export class TeamsHandler implements IAuthenticationStrategy { } // Delete any existing invite for the same email and team - await Token.getInstance().deleteOne({ + const existingInviteQuery: Indexable = { tokenType: TokenType.TEAM_INVITE_TOKEN, - // @ts-expect-error Unsafe nested property access 'data.teamId': teamId, 'data.email': email, - }); + }; + + await Token.getInstance().deleteOne(existingInviteQuery); const invitation = await this.createUserInvitation({ teamId, @@ -645,12 +647,13 @@ export class TeamsHandler implements IAuthenticationStrategy { } // Delete any existing invite for the same email and team - await Token.getInstance().deleteOne({ + const invitationQuery: Indexable = { tokenType: TokenType.TEAM_INVITE_TOKEN, - // @ts-expect-error Unsafe nested property access 'data.teamId': teamId, 'data.email': email, - }); + }; + + await Token.getInstance().deleteOne(invitationQuery); return 'OK'; } @@ -682,17 +685,14 @@ export class TeamsHandler implements IAuthenticationStrategy { ); } - const invites = await Token.getInstance().findMany({ + const teamInvitesQuery: Indexable = { tokenType: TokenType.TEAM_INVITE_TOKEN, - // @ts-expect-error Unsafe nested property access 'data.teamId': teamId, - }); + }; - const count = await Token.getInstance().countDocuments({ - tokenType: TokenType.TEAM_INVITE_TOKEN, - // @ts-expect-error Unsafe nested property access - 'data.teamId': teamId, - }); + const invites = await Token.getInstance().findMany(teamInvitesQuery); + + const count = await Token.getInstance().countDocuments(teamInvitesQuery); return { invites, diff --git a/modules/database/src/adapters/DatabaseAdapter.ts b/modules/database/src/adapters/DatabaseAdapter.ts index 9edba2d83..5268b9ad7 100644 --- a/modules/database/src/adapters/DatabaseAdapter.ts +++ b/modules/database/src/adapters/DatabaseAdapter.ts @@ -472,16 +472,29 @@ export abstract class DatabaseAdapter { canModify: 'Everything', canDelete: true, } as const; + + const setDefaultPermission = ( + permissions: NonNullable< + NonNullable['permissions'] + >, + key: K, + ) => { + if (!Object.prototype.hasOwnProperty.call(permissions, key)) { + permissions[key] = defaultPermissions[key]; + } + }; + if (isNil(schema.modelOptions.conduit)) schema.modelOptions.conduit = {}; + if (isNil(schema.modelOptions.conduit.permissions)) { - schema.modelOptions.conduit!.permissions = defaultPermissions; + schema.modelOptions.conduit.permissions = { ...defaultPermissions }; } else { - Object.keys(defaultPermissions).forEach(perm => { - if (!schema.modelOptions.conduit!.permissions!.hasOwnProperty(perm)) { - // @ts-ignore - schema.modelOptions.conduit!.permissions![perm] = - defaultPermissions[perm as keyof typeof defaultPermissions]; - } + const permissions = schema.modelOptions.conduit.permissions; + const defaultPermissionKeys = Object.keys(defaultPermissions) as Array< + keyof typeof defaultPermissions + >; + defaultPermissionKeys.forEach(perm => { + setDefaultPermission(permissions, perm); }); } return schema; diff --git a/modules/database/src/adapters/utils/database-transform-utils.ts b/modules/database/src/adapters/utils/database-transform-utils.ts index 6ec5768d0..fc7dbffd9 100644 --- a/modules/database/src/adapters/utils/database-transform-utils.ts +++ b/modules/database/src/adapters/utils/database-transform-utils.ts @@ -120,9 +120,8 @@ export function extractFieldProperties( } else if (objectField.hasOwnProperty('unique') && objectField.unique) { res.unique = objectField.unique ?? false; res.allowNull = false; - } else if (objectField.hasOwnProperty('required') && objectField.required) { - // @ts-expect-error - res.allowNull = !objectField.required ?? true; + } else if (objectField.hasOwnProperty('required') && isBoolean(objectField.required)) { + res.allowNull = !objectField.required; } return res; diff --git a/modules/database/src/utils/utilities.ts b/modules/database/src/utils/utilities.ts index 524fb2c94..46cf4201a 100644 --- a/modules/database/src/utils/utilities.ts +++ b/modules/database/src/utils/utilities.ts @@ -199,22 +199,28 @@ function validateCrudOperations(crudOperations: CrudOperations) { throw new Error(`CMS field 'crudOperations' must be of type Object`); Object.keys(crudOperations).forEach(op => { - if (!allowedCrudOperations.includes(op as keyof CrudOperations)) { + const crudOperation = op as keyof CrudOperations; + + if (!allowedCrudOperations.includes(crudOperation)) { throw new Error(`Unrecognized CRUD operation '${op}' provided`); } - // @ts-ignore - if (!isObject(crudOperations[op])) { + + const operationConfig = crudOperations[crudOperation]; + + if (!isObject(operationConfig)) { throw new Error(`Crud operation field '${op}' must be of type Object`); } - // @ts-ignore - Object.keys(crudOperations[op]).forEach(opField => { - if (!['enabled', 'authenticated'].includes(opField)) { + + Object.keys(operationConfig).forEach(opField => { + const crudOperationField = opField as keyof typeof operationConfig; + + if (!['enabled', 'authenticated'].includes(crudOperationField)) { throw new Error( `Unrecognized crud operation field '${opField}' for operation '${op}' provided`, ); } - // @ts-ignore - if (!isBoolean(crudOperations[op][opField])) { + + if (!isBoolean(operationConfig[crudOperationField])) { throw new Error( `Crud operation field '${opField}' for operation '${op}' must be of type Boolean`, ); diff --git a/modules/email/src/email-provider/transports/mandrill/MandrilProvider.ts b/modules/email/src/email-provider/transports/mandrill/MandrilProvider.ts index c2966629a..61878b649 100644 --- a/modules/email/src/email-provider/transports/mandrill/MandrilProvider.ts +++ b/modules/email/src/email-provider/transports/mandrill/MandrilProvider.ts @@ -9,8 +9,6 @@ import { getHandleBarsValues } from '../../utils/index.js'; import { UpdateEmailTemplate } from '../../interfaces/UpdateEmailTemplate.js'; import { MandrillTemplate } from '../../interfaces/mandrill/MandrillTemplate.js'; -// @ts-expect-error -// missing typings for nodemailer-mandrill-transport import mandrillTransport from 'nodemailer-mandrill-transport'; import { Indexable } from '@conduitplatform/grpc-sdk'; diff --git a/modules/email/src/types/nodemailer-mandrill-transport.d.ts b/modules/email/src/types/nodemailer-mandrill-transport.d.ts new file mode 100644 index 000000000..38818e933 --- /dev/null +++ b/modules/email/src/types/nodemailer-mandrill-transport.d.ts @@ -0,0 +1,13 @@ +declare module 'nodemailer-mandrill-transport' { + import type { Transport } from 'nodemailer'; + + interface MandrillTransportOptions { + auth: { + apiKey: string; + }; + } + + function mandrillTransport(options: MandrillTransportOptions): Transport; + + export default mandrillTransport; +} diff --git a/modules/storage/src/utils/index.ts b/modules/storage/src/utils/index.ts index 413665419..324cb08c5 100644 --- a/modules/storage/src/utils/index.ts +++ b/modules/storage/src/utils/index.ts @@ -13,17 +13,25 @@ import { randomUUID } from 'node:crypto'; import { ConfigController } from '@conduitplatform/module-tools'; import { status } from '@grpc/grpc-js'; -export async function streamToBuffer(readableStream: any): Promise { - return new Promise((resolve, reject) => { +export async function streamToBuffer( + readableStream?: NodeJS.ReadableStream, +): Promise { + return new Promise((resolve, reject) => { + if (!readableStream) { + reject(new Error('Readable stream is undefined')); + return; + } + const chunks: Buffer[] = []; - readableStream.on('data', (data: any) => { - chunks.push(data instanceof Buffer ? data : Buffer.from(data)); + + readableStream.on('data', (data: string | Buffer | Uint8Array) => { + chunks.push(Buffer.isBuffer(data) ? data : Buffer.from(data)); }); + readableStream.on('end', () => { - // shouldn't really provide an error - // @ts-expect-error - resolve(Buffer.concat(chunks)); + resolve(Buffer.concat(chunks as unknown as readonly Uint8Array[])); }); + readableStream.on('error', reject); }); }