diff --git a/packages/api/src/apollo.ts b/packages/api/src/apollo.ts index 1527fa9ce..65435b78d 100644 --- a/packages/api/src/apollo.ts +++ b/packages/api/src/apollo.ts @@ -4,30 +4,29 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ +import { makeExecutableSchema } from '@graphql-tools/schema' +import * as Sentry from '@sentry/node' import { ContextFunction } from 'apollo-server-core' -import { ClaimsToSet, ResolverContext } from './resolvers/types' -import { SetClaimsRole } from './utils/dictionary' -import { Knex } from 'knex' +import { ApolloServer } from 'apollo-server-express' import { ExpressContext } from 'apollo-server-express/dist/ApolloServer' import * as jwt from 'jsonwebtoken' -import { kx } from './datalayer/knex_config' -import { tracer } from './tracing' -import { env } from './env' +import { Knex } from 'knex' import { promisify } from 'util' -import { buildLogger } from './utils/logger' -import { ApolloServer } from 'apollo-server-express' -import { makeExecutableSchema } from '@graphql-tools/schema' -import typeDefs from './schema' -import { sanitizeDirectiveTransformer } from './directives' -import { functionResolvers } from './resolvers/function_resolvers' -import ScalarResolvers from './scalars' -import * as Sentry from '@sentry/node' +import { kx } from './datalayer/knex_config' import { createPubSubClient } from './datalayer/pubsub' +import { sanitizeDirectiveTransformer } from './directives' +import { env } from './env' +import { functionResolvers } from './resolvers/function_resolvers' +import { ClaimsToSet, ResolverContext } from './resolvers/types' +import ScalarResolvers from './scalars' +import typeDefs from './schema' import { initModels } from './server' +import { tracer } from './tracing' import { getClaimsByToken, setAuthInCookie } from './utils/auth' +import { SetClaimsRole } from './utils/dictionary' +import { logger } from './utils/logger' const signToken = promisify(jwt.sign) -const logger = buildLogger('app.dispatch') const pubsub = createPubSubClient() const resolvers = { diff --git a/packages/api/src/datalayer/helpers.ts b/packages/api/src/datalayer/helpers.ts index ea0ba4334..f357f3c6c 100644 --- a/packages/api/src/datalayer/helpers.ts +++ b/packages/api/src/datalayer/helpers.ts @@ -11,7 +11,7 @@ import { snakeCase } from 'snake-case' import { buildLogger } from '../utils/logger' import { SetClaimsRole } from '../utils/dictionary' -const logger = buildLogger('datalayer') +export const logger = buildLogger('datalayer') export const setClaims = async ( tx: Knex.Transaction, diff --git a/packages/api/src/datalayer/highlight/index.ts b/packages/api/src/datalayer/highlight/index.ts index 8bd8fb06a..6ef94805a 100644 --- a/packages/api/src/datalayer/highlight/index.ts +++ b/packages/api/src/datalayer/highlight/index.ts @@ -5,7 +5,7 @@ import DataModel, { DataModelError, MAX_RECORDS_LIMIT } from '../model' import { CreateSet, HighlightData, keys as modelKeys, UpdateSet } from './model' import { Table } from '../../utils/dictionary' import { Knex } from 'knex' -import { ENABLE_DB_REQUEST_LOGGING, globalCounter } from '../helpers' +import { ENABLE_DB_REQUEST_LOGGING, globalCounter, logger } from '../helpers' import DataLoader from 'dataloader' class HighlightModel extends DataModel { @@ -40,11 +40,11 @@ class HighlightModel extends DataModel { } const result = keys.map((key) => keyMap[key]) if (result.length !== keys.length) { - console.error('DataModel error: count mismatch ', keys, result) + logger.error('DataModel error: count mismatch ', keys, result) } return result } catch (e) { - console.error('DataModel error ', e) + logger.error('DataModel error ', e) throw e } }, diff --git a/packages/api/src/datalayer/links/index.ts b/packages/api/src/datalayer/links/index.ts index 08ba9e266..7bf69f56e 100644 --- a/packages/api/src/datalayer/links/index.ts +++ b/packages/api/src/datalayer/links/index.ts @@ -20,7 +20,12 @@ import { SortOrder, SortParams, } from '../../generated/graphql' -import { ENABLE_DB_REQUEST_LOGGING, globalCounter, logMethod } from '../helpers' +import { + ENABLE_DB_REQUEST_LOGGING, + globalCounter, + logMethod, + logger, +} from '../helpers' import DataLoader from 'dataloader' import { ArticleData } from '../article/model' import { InFilter, LabelFilter, ReadFilter } from '../../utils/search' @@ -147,11 +152,11 @@ class UserArticleModel extends DataModel< // logger.debug('\n\n\n\n\nResult for userId_articleId_load', { keys, result }); if (result.length !== keys.length) { - console.error('DataModel error: count mismatch ', keys, result) + logger.error('DataModel error: count mismatch ', keys, result) } return result } catch (e) { - console.error('DataModel error: ', e) + logger.error('DataModel error: ', e) throw e } }, @@ -201,11 +206,11 @@ class UserArticleModel extends DataModel< // logger.debug('\n\n\n\n\nResult for userArticleId_stats_load', { keys, result }); if (result.length !== keys.length) { - console.error('DataModel error: count mismatch ', keys, result) + logger.error('DataModel error: count mismatch ', keys, result) } return result } catch (e) { - console.error('DataModel error: ', e) + logger.error('DataModel error: ', e) throw e } }, diff --git a/packages/api/src/datalayer/model.ts b/packages/api/src/datalayer/model.ts index f2ebfe2d2..ab17480f6 100644 --- a/packages/api/src/datalayer/model.ts +++ b/packages/api/src/datalayer/model.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import DataLoader from 'dataloader' import { Knex } from 'knex' -import { ENABLE_DB_REQUEST_LOGGING, globalCounter } from './helpers' +import { ENABLE_DB_REQUEST_LOGGING, globalCounter, logger } from './helpers' export enum DataModelError { notFound = 'NOT_FOUND', @@ -46,11 +46,11 @@ abstract class DataModel< } const result = keys.map((key) => keyMap[key]) if (result.length !== keys.length) { - console.error('DataModel error: count mismatch ', keys, result) + logger.error('DataModel error: count mismatch ', keys, result) } return result } catch (e) { - console.error('DataModel error: ', e) + logger.error('DataModel error: ', e) throw e } }, diff --git a/packages/api/src/datalayer/user/index.ts b/packages/api/src/datalayer/user/index.ts index 5112c08b6..4fa382ba0 100644 --- a/packages/api/src/datalayer/user/index.ts +++ b/packages/api/src/datalayer/user/index.ts @@ -12,7 +12,12 @@ import { } from './model' import DataModel, { DataModelError, MAX_RECORDS_LIMIT } from '../model' import { Knex } from 'knex' -import { ENABLE_DB_REQUEST_LOGGING, globalCounter, logMethod } from '../helpers' +import { + ENABLE_DB_REQUEST_LOGGING, + globalCounter, + logMethod, + logger, +} from '../helpers' import { Table } from '../../utils/dictionary' import DataLoader from 'dataloader' import { Partialize } from '../../util' @@ -57,11 +62,11 @@ class UserModel extends DataModel { } const result = keys.map((key) => keyMap[key]) if (result.length !== keys.length) { - console.error('DataModel error: count mismatch ', keys, result) + logger.error('DataModel error: count mismatch ', keys, result) } return result } catch (e) { - console.error('DataModel error: ', e) + logger.error('DataModel error: ', e) throw e } }, diff --git a/packages/api/src/datalayer/user_friends/index.ts b/packages/api/src/datalayer/user_friends/index.ts index 8ff18e0d8..f568f15b7 100644 --- a/packages/api/src/datalayer/user_friends/index.ts +++ b/packages/api/src/datalayer/user_friends/index.ts @@ -8,7 +8,12 @@ import { import DataModel, { MAX_RECORDS_LIMIT } from '../model' import { Knex } from 'knex' import { Table } from '../../utils/dictionary' -import { ENABLE_DB_REQUEST_LOGGING, globalCounter, logMethod } from '../helpers' +import { + ENABLE_DB_REQUEST_LOGGING, + globalCounter, + logMethod, + logger, +} from '../helpers' import DataLoader from 'dataloader' class UserFriendModel extends DataModel { @@ -43,7 +48,7 @@ class UserFriendModel extends DataModel { // logger.debug('\n\n\n\n\nResult for userId_articleId_load', { keys, result }); if (result.length !== keys.length) { - console.error('DataModel error: count mismatch ', keys, result) + logger.error('DataModel error: count mismatch ', keys, result) } return result }, diff --git a/packages/api/src/elastic/highlights.ts b/packages/api/src/elastic/highlights.ts index 8885bfc1b..0ecf95d2c 100644 --- a/packages/api/src/elastic/highlights.ts +++ b/packages/api/src/elastic/highlights.ts @@ -1,8 +1,7 @@ import { errors } from '@elastic/elasticsearch' import { EntityType } from '../datalayer/pubsub' -import { buildLogger } from '../utils/logger' import { SortBy, SortOrder, SortParams } from '../utils/search' -import { client, INDEX_ALIAS } from './index' +import { client, INDEX_ALIAS, logger } from './index' import { Highlight, Page, @@ -12,8 +11,6 @@ import { SearchResponse, } from './types' -const logger = buildLogger('elasticsearch') - export const addHighlightToPage = async ( id: string, highlight: Highlight, diff --git a/packages/api/src/elastic/index.ts b/packages/api/src/elastic/index.ts index d36e079b1..298ef8b40 100644 --- a/packages/api/src/elastic/index.ts +++ b/packages/api/src/elastic/index.ts @@ -1,6 +1,7 @@ -import { env } from '../env' import { Client } from '@elastic/elasticsearch' import { readFileSync } from 'fs' +import { env } from '../env' +import { buildLogger } from '../utils/logger' export const INDEX_ALIAS = 'pages_alias' export const client = new Client({ @@ -13,6 +14,7 @@ export const client = new Client({ }, }) const INDEX_NAME = 'pages' +export const logger = buildLogger('elasticsearch') const createIndex = async (): Promise => { // read index settings from file @@ -31,22 +33,22 @@ const createIndex = async (): Promise => { export const initElasticsearch = async (): Promise => { try { const response = await client.info() - console.log('elastic info: ', response) + logger.info('elastic info: ', response) // check if index exists const { body: indexExists } = await client.indices.exists({ index: INDEX_ALIAS, }) if (!indexExists) { - console.log('creating index...') + logger.info('creating index...') await createIndex() - console.log('refreshing index...') + logger.info('refreshing index...') await refreshIndex() } - console.log('elastic client is ready') + logger.info('elastic client is ready') } catch (e) { - console.error('failed to init elasticsearch', e) + logger.error('failed to init elasticsearch', e) throw e } } @@ -56,9 +58,9 @@ export const refreshIndex = async (): Promise => { const { body } = await client.indices.refresh({ index: INDEX_ALIAS, }) - console.log('elastic refresh: ', body) + logger.info('elastic refresh: ', body) } catch (e) { - console.error('failed to refresh elastic index', e) + logger.error('failed to refresh elastic index', e) throw e } } diff --git a/packages/api/src/elastic/labels.ts b/packages/api/src/elastic/labels.ts index 030a0e460..5864a0dff 100644 --- a/packages/api/src/elastic/labels.ts +++ b/packages/api/src/elastic/labels.ts @@ -1,11 +1,8 @@ import { errors } from '@elastic/elasticsearch' import { EntityType } from '../datalayer/pubsub' -import { buildLogger } from '../utils/logger' -import { client, INDEX_ALIAS } from './index' +import { client, INDEX_ALIAS, logger } from './index' import { Label, PageContext } from './types' -const logger = buildLogger('elasticsearch') - export const addLabelInPage = async ( pageId: string, label: Label, diff --git a/packages/api/src/elastic/pages.ts b/packages/api/src/elastic/pages.ts index a028abcdb..cc8f4b947 100644 --- a/packages/api/src/elastic/pages.ts +++ b/packages/api/src/elastic/pages.ts @@ -3,7 +3,6 @@ import { BuiltQuery, ESBuilder, esBuilder } from 'elastic-ts' import { EntityType } from '../datalayer/pubsub' import { BulkActionType } from '../generated/graphql' import { wordsCount } from '../utils/helpers' -import { buildLogger } from '../utils/logger' import { DateFilter, FieldFilter, @@ -16,7 +15,7 @@ import { SortBy, SortOrder, } from '../utils/search' -import { client, INDEX_ALIAS } from './index' +import { client, INDEX_ALIAS, logger } from './index' import { ArticleSavingRequestStatus, Label, @@ -28,8 +27,6 @@ import { SearchResponse, } from './types' -const logger = buildLogger('elasticsearch') - const appendQuery = (builder: ESBuilder, query: string): ESBuilder => { interface Field { field: string diff --git a/packages/api/src/elastic/recommendation.ts b/packages/api/src/elastic/recommendation.ts index dd327dd6f..3caa544e6 100644 --- a/packages/api/src/elastic/recommendation.ts +++ b/packages/api/src/elastic/recommendation.ts @@ -1,4 +1,4 @@ -import { buildLogger } from '../utils/logger' +import { logger } from '.' import { createPage, getPageByParam, updatePage } from './pages' import { ArticleSavingRequestStatus, @@ -7,8 +7,6 @@ import { Recommendation, } from './types' -const logger = buildLogger('app.dispatch') - export const addRecommendation = async ( ctx: PageContext, page: Page, diff --git a/packages/api/src/events/entity_created.ts b/packages/api/src/events/entity_created.ts index 63a0f53a1..7c2bb4e78 100644 --- a/packages/api/src/events/entity_created.ts +++ b/packages/api/src/events/entity_created.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { PubSub } from '@google-cloud/pubsub' - import { BaseEntity, EntitySubscriberInterface, EventSubscriber, InsertEvent, } from 'typeorm' - import { env } from '../env' +import { logger } from '../utils/logger' const TOPIC_NAME = 'EntityCreated' @@ -24,7 +23,7 @@ export class PublishEntitySubscriber implements EntitySubscriberInterface { }) if (env.dev.isLocal) { - console.log('PublishEntitySubscriber', msg) + logger.info('PublishEntitySubscriber', msg) return } @@ -32,7 +31,7 @@ export class PublishEntitySubscriber implements EntitySubscriberInterface { .topic(TOPIC_NAME) .publish(Buffer.from(msg)) .catch((err) => { - console.error('PublishEntitySubscriber error publishing event', err) + logger.error('PublishEntitySubscriber error publishing event', err) }) } } diff --git a/packages/api/src/events/reports/content_display_report_created.ts b/packages/api/src/events/reports/content_display_report_created.ts index add7b1d80..d89dcfc22 100644 --- a/packages/api/src/events/reports/content_display_report_created.ts +++ b/packages/api/src/events/reports/content_display_report_created.ts @@ -4,9 +4,9 @@ import { EventSubscriber, InsertEvent, } from 'typeorm' - import { ContentDisplayReport } from '../../entity/reports/content_display_report' import { env } from '../../env' +import { logger } from '../../utils/logger' import { sendEmail } from '../../utils/sendEmail' @EventSubscriber() @@ -23,7 +23,7 @@ export class ContentDisplayReportSubscriber ${report.user.id} for URL: ${report.originalUrl} ${report.reportComment}` - console.log(message) + logger.info(message) if (!env.dev.isLocal) { // If we are in the local environment, just log a message, otherwise email the report diff --git a/packages/api/src/events/user/user_created.ts b/packages/api/src/events/user/user_created.ts index a8c424147..af720bf1d 100644 --- a/packages/api/src/events/user/user_created.ts +++ b/packages/api/src/events/user/user_created.ts @@ -3,13 +3,13 @@ import { EventSubscriber, InsertEvent, } from 'typeorm' - -import { Profile } from '../../entity/profile' -import { IntercomClient } from '../../utils/intercom' import { createPubSubClient } from '../../datalayer/pubsub' +import { Profile } from '../../entity/profile' import { env } from '../../env' -import { analytics } from '../../utils/analytics' import { addPopularReadsForNewUser } from '../../services/popular_reads' +import { analytics } from '../../utils/analytics' +import { IntercomClient } from '../../utils/intercom' +import { logger } from '../../utils/logger' @EventSubscriber() export class CreateIntercomAccount @@ -56,7 +56,7 @@ export class IdentifySegmentUser implements EntitySubscriberInterface { }, }) } catch (error) { - console.log('error in sign up', error) + logger.info('error in sign up', error) } return Promise.resolve() } diff --git a/packages/api/src/resolvers/function_resolvers.ts b/packages/api/src/resolvers/function_resolvers.ts index 559673b21..56035f7f9 100644 --- a/packages/api/src/resolvers/function_resolvers.ts +++ b/packages/api/src/resolvers/function_resolvers.ts @@ -19,6 +19,7 @@ import { } from '../generated/graphql' import { userDataToUser, validatedDate, wordsCount } from '../utils/helpers' import { createImageProxyUrl } from '../utils/imageproxy' +import { logger } from '../utils/logger' import { contentReaderForPage, generateDownloadSignedUrl, @@ -317,7 +318,7 @@ export const functionResolvers = { if (a && a.image) { a.image = createImageProxyUrl(a.image, 0, 180) } else { - console.log( + logger.info( 'error getting article for feedItem', feedArticle.userId, feedArticle.articleId diff --git a/packages/api/src/resolvers/importers/uploadImportFileResolver.ts b/packages/api/src/resolvers/importers/uploadImportFileResolver.ts index 4b37cfb2f..122ef69ee 100644 --- a/packages/api/src/resolvers/importers/uploadImportFileResolver.ts +++ b/packages/api/src/resolvers/importers/uploadImportFileResolver.ts @@ -1,27 +1,25 @@ -import { authorized } from '../../utils/helpers' +import { DateTime } from 'luxon' +import { v4 as uuidv4 } from 'uuid' +import { User } from '../../entity/user' +import { getRepository } from '../../entity/utils' +import { env } from '../../env' import { - UploadImportFileErrorCode, MutationUploadImportFileArgs, UploadImportFileError, + UploadImportFileErrorCode, UploadImportFileSuccess, } from '../../generated/graphql' -import { getRepository } from '../../entity/utils' -import { User } from '../../entity/user' import { analytics } from '../../utils/analytics' -import { env } from '../../env' -import { DateTime } from 'luxon' +import { authorized } from '../../utils/helpers' +import { logger } from '../../utils/logger' import { countOfFilesWithPrefix, generateUploadSignedUrl, } from '../../utils/uploads' -import { v4 as uuidv4 } from 'uuid' -import { buildLogger } from '../../utils/logger' const MAX_DAILY_UPLOADS = 4 const VALID_CONTENT_TYPES = ['text/csv', 'application/zip'] -const logger = buildLogger('app.dispatch') - const extensionForContentType = (contentType: string) => { switch (contentType) { case 'text/csv': diff --git a/packages/api/src/resolvers/links/index.ts b/packages/api/src/resolvers/links/index.ts index 8aa224acc..f0105554a 100644 --- a/packages/api/src/resolvers/links/index.ts +++ b/packages/api/src/resolvers/links/index.ts @@ -18,15 +18,10 @@ export const updateLinkShareInfoResolver = authorized< UpdateLinkShareInfoSuccess, UpdateLinkShareInfoError, MutationUpdateLinkShareInfoArgs ->(async (_obj, args, { models, claims, authTrx }) => { +>(async (_obj, args, { models, claims, authTrx, log }) => { const { title, description } = args.input - console.log( - 'updateLinkShareInfoResolver', - args.input.linkId, - title, - description - ) + log.info('updateLinkShareInfoResolver', args.input.linkId, title, description) // TEMP: because the old API uses articles instead of Links, we are actually // getting an article ID here and need to map it to a link ID. When the API @@ -62,8 +57,8 @@ export const setLinkArchivedResolver = authorized< ArchiveLinkSuccess, ArchiveLinkError, MutationSetLinkArchivedArgs ->(async (_obj, args, { claims, pubsub }) => { - console.log('setLinkArchivedResolver', args.input.linkId) +>(async (_obj, args, { claims, pubsub, log }) => { + log.info('setLinkArchivedResolver', args.input.linkId) analytics.track({ userId: claims.uid, diff --git a/packages/api/src/resolvers/newsletters/index.ts b/packages/api/src/resolvers/newsletters/index.ts index bc4544d0f..58919c985 100644 --- a/packages/api/src/resolvers/newsletters/index.ts +++ b/packages/api/src/resolvers/newsletters/index.ts @@ -1,4 +1,7 @@ -import { authorized } from '../../utils/helpers' +import { NewsletterEmail } from '../../entity/newsletter_email' +import { User } from '../../entity/user' +import { getRepository } from '../../entity/utils' +import { env } from '../../env' import { CreateNewsletterEmailError, CreateNewsletterEmailErrorCode, @@ -16,18 +19,15 @@ import { deleteNewsletterEmail, getNewsletterEmails, } from '../../services/newsletters' -import { NewsletterEmail } from '../../entity/newsletter_email' -import { analytics } from '../../utils/analytics' -import { env } from '../../env' -import { User } from '../../entity/user' import { unsubscribeAll } from '../../services/subscriptions' -import { getRepository } from '../../entity/utils' +import { analytics } from '../../utils/analytics' +import { authorized } from '../../utils/helpers' export const createNewsletterEmailResolver = authorized< CreateNewsletterEmailSuccess, CreateNewsletterEmailError ->(async (_parent, _args, { claims }) => { - console.log('createNewsletterEmailResolver') +>(async (_parent, _args, { claims, log }) => { + log.info('createNewsletterEmailResolver') analytics.track({ userId: claims.uid, event: 'newsletter_email_address_created', @@ -46,7 +46,7 @@ export const createNewsletterEmailResolver = authorized< }, } } catch (e) { - console.log(e) + log.info(e) return { errorCodes: [CreateNewsletterEmailErrorCode.BadRequest], @@ -57,8 +57,8 @@ export const createNewsletterEmailResolver = authorized< export const newsletterEmailsResolver = authorized< NewsletterEmailsSuccess, NewsletterEmailsError ->(async (_parent, _args, { claims }) => { - console.log('newsletterEmailsResolver') +>(async (_parent, _args, { claims, log }) => { + log.info('newsletterEmailsResolver') try { const user = await getRepository(User).findOneBy({ @@ -79,7 +79,7 @@ export const newsletterEmailsResolver = authorized< })), } } catch (e) { - console.log(e) + log.info(e) return { errorCodes: [NewsletterEmailsErrorCode.BadRequest], @@ -91,8 +91,8 @@ export const deleteNewsletterEmailResolver = authorized< DeleteNewsletterEmailSuccess, DeleteNewsletterEmailError, MutationDeleteNewsletterEmailArgs ->(async (_parent, args, { claims }) => { - console.log('deleteNewsletterEmailResolver') +>(async (_parent, args, { claims, log }) => { + log.info('deleteNewsletterEmailResolver') analytics.track({ userId: claims.uid, event: 'newsletter_email_address_deleted', @@ -139,7 +139,7 @@ export const deleteNewsletterEmailResolver = authorized< } } } catch (e) { - console.log(e) + log.info(e) return { errorCodes: [DeleteNewsletterEmailErrorCode.BadRequest], diff --git a/packages/api/src/resolvers/reaction/index.ts b/packages/api/src/resolvers/reaction/index.ts index e321af6b4..25687a55f 100644 --- a/packages/api/src/resolvers/reaction/index.ts +++ b/packages/api/src/resolvers/reaction/index.ts @@ -1,14 +1,14 @@ import { Merge } from '../../util' import { authorized } from '../../utils/helpers' import { - CreateReactionSuccess, CreateReactionError, - MutationCreateReactionArgs, CreateReactionErrorCode, - Reaction, - DeleteReactionErrorCode, + CreateReactionSuccess, DeleteReactionError, + DeleteReactionErrorCode, + MutationCreateReactionArgs, MutationDeleteReactionArgs, + Reaction, } from './../../generated/graphql' export type PartialReaction = Omit @@ -93,7 +93,7 @@ export const createReactionResolver = authorized< reaction: reaction as PartialReaction, } } catch (err) { - console.log(err) + log.info(err) return { errorCodes: [CreateReactionErrorCode.NotFound], } diff --git a/packages/api/src/resolvers/reminders/index.ts b/packages/api/src/resolvers/reminders/index.ts index 02c5cd236..9b33134b6 100644 --- a/packages/api/src/resolvers/reminders/index.ts +++ b/packages/api/src/resolvers/reminders/index.ts @@ -24,6 +24,7 @@ import { setLinkArchived } from '../../services/archive_link' import { analytics } from '../../utils/analytics' import { deleteTask, enqueueReminder } from '../../utils/createTask' import { authorized } from '../../utils/helpers' +import { logger } from '../../utils/logger' import { DataModels } from '../types' const validScheduleTime = (str: string): Date | undefined => { @@ -99,7 +100,7 @@ export const createReminderResolver = authorized< } const taskName = await groupReminders(scheduledTime, uid, models) - console.log('scheduled task name', taskName) + log.info('scheduled task name', taskName) // insert reminder to db const reminder = await models.reminder.create({ @@ -111,7 +112,7 @@ export const createReminderResolver = authorized< remindAt: scheduledTime, elasticPageId: pageId, }) - console.log('created reminder', reminder) + log.info('created reminder', reminder) return { reminder: { @@ -122,7 +123,7 @@ export const createReminderResolver = authorized< }, } } catch (e) { - console.log('error creating reminder', e) + log.info('error creating reminder', e) return { errorCodes: [CreateReminderErrorCode.BadRequest], @@ -138,7 +139,7 @@ const archivePage = async (uid: string, page: Page) => { try { await setLinkArchived(uid, page.id, true) } catch (e) { - console.log('error archiving link', e) + logger.info('error archiving link', e) } } diff --git a/packages/api/src/resolvers/send_install_instructions/index.ts b/packages/api/src/resolvers/send_install_instructions/index.ts index 2f050d4f8..e42e92687 100644 --- a/packages/api/src/resolvers/send_install_instructions/index.ts +++ b/packages/api/src/resolvers/send_install_instructions/index.ts @@ -1,13 +1,13 @@ +import { User } from '../../entity/user' +import { env } from '../../env' import { SendInstallInstructionsError, SendInstallInstructionsErrorCode, SendInstallInstructionsSuccess, } from '../../generated/graphql' +import { AppDataSource } from '../../server' import { authorized } from '../../utils/helpers' import { sendEmail } from '../../utils/sendEmail' -import { AppDataSource } from '../../server' -import { User } from '../../entity/user' -import { env } from '../../env' const INSTALL_INSTRUCTIONS_EMAIL_TEMPLATE_ID = 'd-c576bdc3b9a849dab250655ba14c7794' @@ -15,7 +15,7 @@ const INSTALL_INSTRUCTIONS_EMAIL_TEMPLATE_ID = export const sendInstallInstructionsResolver = authorized< SendInstallInstructionsSuccess, SendInstallInstructionsError ->(async (_parent, _args, { claims }) => { +>(async (_parent, _args, { claims, log }) => { try { const user = await AppDataSource.getRepository(User).findOneBy({ id: claims.uid, @@ -37,7 +37,7 @@ export const sendInstallInstructionsResolver = authorized< sent: sendInstallInstructions, } } catch (e) { - console.log(e) + log.info(e) return { errorCodes: [SendInstallInstructionsErrorCode.BadRequest], diff --git a/packages/api/src/resolvers/user/index.ts b/packages/api/src/resolvers/user/index.ts index 4450ad910..f010a3dc8 100644 --- a/packages/api/src/resolvers/user/index.ts +++ b/packages/api/src/resolvers/user/index.ts @@ -1,3 +1,8 @@ +import * as jwt from 'jsonwebtoken' +import { deletePagesByParam } from '../../elastic/pages' +import { User as UserEntity } from '../../entity/user' +import { setClaims } from '../../entity/utils' +import { env } from '../../env' import { DeleteAccountError, DeleteAccountErrorCode, @@ -28,16 +33,11 @@ import { UsersError, UsersSuccess, } from '../../generated/graphql' -import { WithDataSourcesContext } from '../types' -import { authorized, userDataToUser } from '../../utils/helpers' -import { env } from '../../env' -import { validateUsername } from '../../utils/usernamePolicy' -import * as jwt from 'jsonwebtoken' -import { createUser } from '../../services/create_user' -import { deletePagesByParam } from '../../elastic/pages' -import { setClaims } from '../../entity/utils' -import { User as UserEntity } from '../../entity/user' import { AppDataSource } from '../../server' +import { createUser } from '../../services/create_user' +import { authorized, userDataToUser } from '../../utils/helpers' +import { validateUsername } from '../../utils/usernamePolicy' +import { WithDataSourcesContext } from '../types' export const updateUserResolver = authorized< UpdateUserSuccess, @@ -184,7 +184,7 @@ export const googleSignupResolver: ResolverFn< Record, WithDataSourcesContext, MutationGoogleSignupArgs -> = async (_obj, { input }, { setAuth }) => { +> = async (_obj, { input }, { setAuth, log }) => { const { email, username, name, bio, sourceUserId, pictureUrl, secret } = input const lowerCasedUsername = username.toLowerCase() @@ -211,7 +211,7 @@ export const googleSignupResolver: ResolverFn< me: userDataToUser({ ...user, profile: { ...profile, private: false } }), } } catch (err) { - console.log('error signing up with google', err) + log.info('error signing up with google', err) if (isErrorWithCode(err)) { return { errorCodes: [err.errorCode as SignupErrorCode] } } @@ -224,12 +224,12 @@ export const logOutResolver: ResolverFn< unknown, WithDataSourcesContext, unknown -> = (_, __, { clearAuth }) => { +> = (_, __, { clearAuth, log }) => { try { clearAuth() return { message: 'User successfully logged out' } } catch (error) { - console.error(error) + log.error(error) return { errorCodes: [LogOutErrorCode.LogOutFailed] } } } diff --git a/packages/api/src/resolvers/user_device_tokens/index.ts b/packages/api/src/resolvers/user_device_tokens/index.ts index 83b5e65a8..a262a2570 100644 --- a/packages/api/src/resolvers/user_device_tokens/index.ts +++ b/packages/api/src/resolvers/user_device_tokens/index.ts @@ -1,4 +1,7 @@ -import { authorized } from '../../utils/helpers' +import { DatabaseError } from 'pg' +import { QueryFailedError } from 'typeorm' +import { UserDeviceToken } from '../../entity/user_device_tokens' +import { env } from '../../env' import { DeviceToken, DeviceTokensError, @@ -9,8 +12,6 @@ import { SetDeviceTokenErrorCode, SetDeviceTokenSuccess, } from '../../generated/graphql' -import { analytics } from '../../utils/analytics' -import { env } from '../../env' import { createDeviceToken, deleteDeviceToken, @@ -18,9 +19,8 @@ import { getDeviceTokenByToken, getDeviceTokensByUserId, } from '../../services/user_device_tokens' -import { UserDeviceToken } from '../../entity/user_device_tokens' -import { QueryFailedError } from 'typeorm' -import { DatabaseError } from 'pg' +import { analytics } from '../../utils/analytics' +import { authorized } from '../../utils/helpers' const PG_UNIQUE_CONSTRAINT_VIOLATION = '23505' @@ -29,12 +29,12 @@ export const setDeviceTokenResolver = authorized< SetDeviceTokenError, MutationSetDeviceTokenArgs >(async (_parent, { input }, { claims: { uid }, log }) => { - console.log('setDeviceTokenResolver', input) + log.info('setDeviceTokenResolver', input) const { id, token } = input if (!id && !token) { - console.log('id or token is required') + log.info('id or token is required') return { errorCodes: [SetDeviceTokenErrorCode.BadRequest], @@ -140,7 +140,7 @@ export const deviceTokensResolver = authorized< }) const deviceTokens = await getDeviceTokensByUserId(uid) - console.log('deviceTokens', deviceTokens) + log.info('deviceTokens', deviceTokens) return { deviceTokens: deviceTokens.map(deviceTokenToData), diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index c35b39302..2444f7c8d 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -16,7 +16,7 @@ import { getClaimsByToken } from '../utils/auth' import { isSiteBlockedForParse } from '../utils/blocked' import { corsConfig } from '../utils/corsConfig' import { enqueueTextToSpeech } from '../utils/createTask' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' import { generateDownloadSignedUrl } from '../utils/uploads' interface SpeechInput { @@ -26,7 +26,6 @@ interface SpeechInput { language?: string } const outputFormats = ['mp3', 'speech-marks', 'speech'] -const logger = buildLogger('app.dispatch') export function articleRouter() { const router = express.Router() diff --git a/packages/api/src/routers/auth/apple_auth.ts b/packages/api/src/routers/auth/apple_auth.ts index c11fef607..c852e9cfd 100644 --- a/packages/api/src/routers/auth/apple_auth.ts +++ b/packages/api/src/routers/auth/apple_auth.ts @@ -3,16 +3,17 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import * as jwt from 'jsonwebtoken' import jwksClient from 'jwks-rsa' +import UserModel from '../../datalayer/user' +import { env, homePageURL } from '../../env' +import { LoginErrorCode } from '../../generated/graphql' +import { logger } from '../../utils/logger' +import { createSsoToken, ssoRedirectURL } from '../../utils/sso' import { DecodeTokenResult } from './auth_types' import { - createWebAuthToken, createPendingUserToken, + createWebAuthToken, suggestedUsername, } from './jwt_helpers' -import { env, homePageURL } from '../../env' -import UserModel from '../../datalayer/user' -import { LoginErrorCode } from '../../generated/graphql' -import { createSsoToken, ssoRedirectURL } from '../../utils/sso' const appleBaseURL = 'https://appleid.apple.com' const audienceName = 'app.omnivore.app' @@ -35,7 +36,7 @@ async function fetchApplePublicKey(kid: string): Promise { }) return key.getPublicKey() } catch (e) { - console.log('fetchApplePublicKey error', e) + logger.error('fetchApplePublicKey error', e) return null } } @@ -67,7 +68,7 @@ export async function decodeAppleToken( } } } catch (e) { - console.log('decodeAppleToken error', e) + logger.error('decodeAppleToken error', e) return { errorCode: 500 } } } @@ -152,7 +153,7 @@ export async function handleAppleWebAuth( return { redirectURL: authFailedRedirect } } } catch (e) { - console.log('handleAppleWebAuth error', e) + logger.info('handleAppleWebAuth error', e) return { redirectURL: authFailedRedirect } } } diff --git a/packages/api/src/routers/auth/auth_router.ts b/packages/api/src/routers/auth/auth_router.ts index 9f2528139..730b6bd7e 100644 --- a/packages/api/src/routers/auth/auth_router.ts +++ b/packages/api/src/routers/auth/auth_router.ts @@ -7,52 +7,50 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { - generateGoogleLoginURL, - googleAuth, - handleGoogleWebAuth, - validateGoogleUser, -} from './google_auth' - +import axios from 'axios' +import cors from 'cors' import type { Request, Response } from 'express' import express from 'express' -import axios from 'axios' -import { env } from '../../env' +import * as jwt from 'jsonwebtoken' import url from 'url' +import { promisify } from 'util' import { kx } from '../../datalayer/knex_config' import UserModel from '../../datalayer/user' -import { buildLogger } from '../../utils/logger' -import { promisify } from 'util' -import * as jwt from 'jsonwebtoken' -import { LoginErrorCode, SignupErrorCode } from '../../generated/graphql' -import { handleAppleWebAuth } from './apple_auth' -import type { AuthProvider } from './auth_types' -import { createMobileAccountCreationResponse } from './mobile/account_creation' -import { corsConfig } from '../../utils/corsConfig' -import cors from 'cors' - import { RegistrationType, StatusType, UserData, } from '../../datalayer/user/model' +import { User } from '../../entity/user' +import { getRepository, setClaims } from '../../entity/utils' +import { env } from '../../env' +import { LoginErrorCode, SignupErrorCode } from '../../generated/graphql' +import { isErrorWithCode } from '../../resolvers' +import { AppDataSource } from '../../server' +import { createUser, getUserByEmail } from '../../services/create_user' +import { + sendConfirmationEmail, + sendPasswordResetEmail, +} from '../../services/send_emails' import { comparePassword, getClaimsByToken, hashPassword, setAuthInCookie, } from '../../utils/auth' -import { createUser, getUserByEmail } from '../../services/create_user' -import { isErrorWithCode } from '../../resolvers' -import { AppDataSource } from '../../server' -import { getRepository, setClaims } from '../../entity/utils' -import { User } from '../../entity/user' -import { - sendConfirmationEmail, - sendPasswordResetEmail, -} from '../../services/send_emails' -import { createWebAuthToken } from './jwt_helpers' +import { corsConfig } from '../../utils/corsConfig' +import { logger } from '../../utils/logger' import { createSsoToken, ssoRedirectURL } from '../../utils/sso' +import { handleAppleWebAuth } from './apple_auth' +import type { AuthProvider } from './auth_types' +import { + generateGoogleLoginURL, + googleAuth, + handleGoogleWebAuth, + validateGoogleUser, +} from './google_auth' +import { createWebAuthToken } from './jwt_helpers' +import { createMobileAccountCreationResponse } from './mobile/account_creation' export interface SignupRequest { email: string @@ -63,7 +61,6 @@ export interface SignupRequest { pictureUrl?: string } -const logger = buildLogger('app.dispatch') const signToken = promisify(jwt.sign) const cookieParams = { @@ -345,7 +342,7 @@ export function authRouter() { const state = JSON.parse((req.query?.state || '') as string) redirectUri = state?.redirect_uri } catch (err) { - console.warn( + logger.warn( 'handleSuccessfulLogin: failed to parse redirect query state param', err ) diff --git a/packages/api/src/routers/auth/google_auth.ts b/packages/api/src/routers/auth/google_auth.ts index c2ed892df..a7c148da7 100644 --- a/packages/api/src/routers/auth/google_auth.ts +++ b/packages/api/src/routers/auth/google_auth.ts @@ -1,12 +1,13 @@ import { google, oauth2_v2 as oauthV2 } from 'googleapis' -import url from 'url' -import { env, homePageURL } from '../../env' import { OAuth2Client } from 'googleapis-common' -import { DecodeTokenResult } from './auth_types' -import { LoginErrorCode } from '../../generated/graphql' +import url from 'url' import UserModel from '../../datalayer/user' -import { createWebAuthToken, createPendingUserToken } from './jwt_helpers' -import { ssoRedirectURL, createSsoToken } from '../../utils/sso' +import { env, homePageURL } from '../../env' +import { LoginErrorCode } from '../../generated/graphql' +import { logger } from '../../utils/logger' +import { createSsoToken, ssoRedirectURL } from '../../utils/sso' +import { DecodeTokenResult } from './auth_types' +import { createPendingUserToken, createWebAuthToken } from './jwt_helpers' export const googleAuthMobile = (): OAuth2Client => new google.auth.OAuth2(env.google.auth.clientId, env.google.auth.secret) @@ -80,7 +81,7 @@ export async function decodeGoogleToken( const sourceUserId = loginTicket.getUserId() || undefined return { email, sourceUserId } } catch (e) { - console.log('decodeGoogleToken error', e) + logger.info('decodeGoogleToken error', e) return { errorCode: 500 } } } @@ -134,7 +135,7 @@ export async function handleGoogleWebAuth( const userId = user?.id if (!userId || !user?.profile) { - console.log( + logger.info( 'user or profile does not exist:', sourceUserId, 'GOOGLE', @@ -171,7 +172,7 @@ export async function handleGoogleWebAuth( return { redirectURL: authFailedRedirect } } } catch (e) { - console.log('handleGoogleWebAuth error', e) + logger.info('handleGoogleWebAuth error', e) return { redirectURL: authFailedRedirect } } } diff --git a/packages/api/src/routers/auth/jwt_helpers.ts b/packages/api/src/routers/auth/jwt_helpers.ts index 667a620a5..11f65f327 100644 --- a/packages/api/src/routers/auth/jwt_helpers.ts +++ b/packages/api/src/routers/auth/jwt_helpers.ts @@ -1,10 +1,11 @@ +import * as cookie from 'cookie' import * as jwt from 'jsonwebtoken' import { promisify } from 'util' -import * as cookie from 'cookie' import { env } from '../../env' +import { logger } from '../../utils/logger' import { - PendingUserTokenPayload, isPendingUserTokenPayload, + PendingUserTokenPayload, } from './auth_types' const signToken = promisify(jwt.sign) @@ -45,7 +46,7 @@ export async function createPendingUserToken( ): Promise { try { const authToken = await signToken(payload, env.server.jwtSecret) - console.log('creating pending user auth token', payload) + logger.info('creating pending user auth token', payload) if (typeof authToken === 'string') { return authToken } else { diff --git a/packages/api/src/routers/auth/mobile/sign_in.ts b/packages/api/src/routers/auth/mobile/sign_in.ts index 323556569..93cf22ea8 100644 --- a/packages/api/src/routers/auth/mobile/sign_in.ts +++ b/packages/api/src/routers/auth/mobile/sign_in.ts @@ -4,6 +4,7 @@ import { StatusType } from '../../../datalayer/user/model' import { getUserByEmail } from '../../../services/create_user' import { sendConfirmationEmail } from '../../../services/send_emails' import { comparePassword } from '../../../utils/auth' +import { logger } from '../../../utils/logger' import { decodeAppleToken } from '../apple_auth' import { AuthProvider, @@ -31,7 +32,7 @@ export async function createMobileSignInResponse( throw new Error(`Missing or unsupported provider ${provider}`) } catch (e) { - console.log('createMobileSignInResponse error', e) + logger.error('createMobileSignInResponse error', e) return authFailedPayload } } @@ -74,7 +75,7 @@ export async function createMobileEmailSignInResponse( json: mobileAuthPayload, } } catch (e) { - console.log('createMobileEmailSignInResponse failed for user', { + logger.error('createMobileEmailSignInResponse failed for user', { email, error: e, }) @@ -117,7 +118,7 @@ async function createAuthResponsePayload( json: mobileAuthPayload, } } catch (e) { - console.log('createAuthResponsePayload error', { + logger.error('createAuthResponsePayload error', { error: e, email: decodedTokenResult.email, }) diff --git a/packages/api/src/routers/auth/mobile/sign_up.ts b/packages/api/src/routers/auth/mobile/sign_up.ts index f21e64d5d..3af00ec93 100644 --- a/packages/api/src/routers/auth/mobile/sign_up.ts +++ b/packages/api/src/routers/auth/mobile/sign_up.ts @@ -1,17 +1,18 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ +import UserModel from '../../../datalayer/user' +import { createUser } from '../../../services/create_user' +import { hashPassword } from '../../../utils/auth' +import { logger } from '../../../utils/logger' import { decodeAppleToken } from '../apple_auth' -import { decodeGoogleToken } from '../google_auth' +import { isValidSignupRequest } from '../auth_router' import { + AuthProvider, DecodeTokenResult, JsonResponsePayload, - AuthProvider, PendingUserTokenPayload, } from '../auth_types' +import { decodeGoogleToken } from '../google_auth' import { createPendingUserToken, suggestedUsername } from '../jwt_helpers' -import UserModel from '../../../datalayer/user' -import { hashPassword } from '../../../utils/auth' -import { createUser } from '../../../services/create_user' -import { isValidSignupRequest } from '../auth_router' export async function createMobileSignUpResponse( isAndroid: boolean, @@ -40,7 +41,7 @@ export async function createMobileSignUpResponse( throw new Error(`Missing or unsupported provider ${provider}`) } catch (e) { - console.log('createMobileSignUpResponse error', e) + logger.info('createMobileSignUpResponse error', e) return signUpFailedPayload } } @@ -73,7 +74,7 @@ export async function createMobileEmailSignUpResponse( json: {}, } } catch (e) { - console.log('error creating mobile email sign up response', e) + logger.info('error creating mobile email sign up response', e) return signUpFailedPayload } } @@ -131,7 +132,7 @@ async function createSignUpResponsePayload( json: { pendingUserToken, pendingUserProfile }, } } catch (e) { - console.log('createSignUpResponsePayload error', e) + logger.info('createSignUpResponsePayload error', e) return signUpFailedPayload } } diff --git a/packages/api/src/routers/integration_router.ts b/packages/api/src/routers/integration_router.ts index a2a667dd0..de46734db 100644 --- a/packages/api/src/routers/integration_router.ts +++ b/packages/api/src/routers/integration_router.ts @@ -4,9 +4,7 @@ import express from 'express' import { env } from '../env' import { getClaimsByToken } from '../utils/auth' import { corsConfig } from '../utils/corsConfig' -import { buildLogger } from '../utils/logger' - -const logger = buildLogger('app.dispatch') +import { logger } from '../utils/logger' export function integrationRouter() { const router = express.Router() @@ -45,8 +43,13 @@ export function integrationRouter() { res.redirect( `https://getpocket.com/auth/authorize?request_token=${code}&redirect_uri=${redirectUri}?pocketToken=${code}` ) - } catch (e) { - logger.info('pocket/request-token exception:', e) + } catch (error) { + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error('pocket/request-token exception:', error) + } + res.redirect( `${env.client.url}/settings/integrations?errorCodes=UNKNOWN` ) diff --git a/packages/api/src/routers/page_router.ts b/packages/api/src/routers/page_router.ts index c6bc2dcfc..f321c07d4 100644 --- a/packages/api/src/routers/page_router.ts +++ b/packages/api/src/routers/page_router.ts @@ -27,14 +27,12 @@ import { titleForFilePath, validateUuid, } from '../utils/helpers' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' import { generateUploadFilePathName, generateUploadSignedUrl, } from '../utils/uploads' -const logger = buildLogger('app.dispatch') - export function pageRouter() { const router = express.Router() diff --git a/packages/api/src/routers/svc/content.ts b/packages/api/src/routers/svc/content.ts index 13ade7a9b..a991d3062 100644 --- a/packages/api/src/routers/svc/content.ts +++ b/packages/api/src/routers/svc/content.ts @@ -12,9 +12,7 @@ import { getPageByParam, updatePage } from '../../elastic/pages' import { Page } from '../../elastic/types' import { ArticleSavingRequestStatus } from '../../generated/graphql' import { initModels } from '../../server' -import { buildLogger } from '../../utils/logger' - -const logger = buildLogger('app.dispatch') +import { logger } from '../../utils/logger' interface UpdateContentMessage { fileId: string diff --git a/packages/api/src/routers/svc/email_attachment.ts b/packages/api/src/routers/svc/email_attachment.ts index 7a7f967bf..86403aa24 100644 --- a/packages/api/src/routers/svc/email_attachment.ts +++ b/packages/api/src/routers/svc/email_attachment.ts @@ -12,7 +12,7 @@ import { updateReceivedEmail } from '../../services/received_emails' import { analytics } from '../../utils/analytics' import { getClaimsByToken } from '../../utils/auth' import { generateSlug } from '../../utils/helpers' -import { buildLogger } from '../../utils/logger' +import { logger } from '../../utils/logger' import { generateUploadFilePathName, generateUploadSignedUrl, @@ -20,8 +20,6 @@ import { makeStorageFilePublic, } from '../../utils/uploads' -const logger = buildLogger('app.dispatch') - export function emailAttachmentRouter() { const router = express.Router() diff --git a/packages/api/src/routers/svc/emails.ts b/packages/api/src/routers/svc/emails.ts index d7b3dd081..9ceb2c913 100644 --- a/packages/api/src/routers/svc/emails.ts +++ b/packages/api/src/routers/svc/emails.ts @@ -1,27 +1,27 @@ +import cors from 'cors' import express from 'express' import { createPubSubClient, readPushSubscription, } from '../../datalayer/pubsub' -import { sendEmail } from '../../utils/sendEmail' -import { analytics } from '../../utils/analytics' -import { getNewsletterEmail } from '../../services/newsletters' import { env } from '../../env' +import { getNewsletterEmail } from '../../services/newsletters' +import { + saveReceivedEmail, + updateReceivedEmail, +} from '../../services/received_emails' +import { saveEmail } from '../../services/save_email' +import { analytics } from '../../utils/analytics' +import { getClaimsByToken } from '../../utils/auth' +import { corsConfig } from '../../utils/corsConfig' +import { logger } from '../../utils/logger' import { generateUniqueUrl, getTitleFromEmailSubject, isProbablyArticle, parseEmailAddress, } from '../../utils/parser' -import { saveEmail } from '../../services/save_email' -import { buildLogger } from '../../utils/logger' -import { - saveReceivedEmail, - updateReceivedEmail, -} from '../../services/received_emails' -import cors from 'cors' -import { corsConfig } from '../../utils/corsConfig' -import { getClaimsByToken } from '../../utils/auth' +import { sendEmail } from '../../utils/sendEmail' interface EmailMessage { from: string @@ -39,8 +39,6 @@ function isEmailMessage(data: any): data is EmailMessage { return 'from' in data && 'to' in data } -const logger = buildLogger('app.dispatch') - export function emailsServiceRouter() { const router = express.Router() diff --git a/packages/api/src/routers/svc/integrations.ts b/packages/api/src/routers/svc/integrations.ts index 6f16e2a58..2b7515ade 100644 --- a/packages/api/src/routers/svc/integrations.ts +++ b/packages/api/src/routers/svc/integrations.ts @@ -13,7 +13,7 @@ import { getRepository } from '../../entity/utils' import { Claims } from '../../resolvers/types' import { getIntegrationService } from '../../services/integrations' import { getClaimsByToken } from '../../utils/auth' -import { buildLogger } from '../../utils/logger' +import { logger } from '../../utils/logger' import { DateFilter } from '../../utils/search' import { createGCSFile } from '../../utils/uploads' @@ -32,8 +32,6 @@ interface ImportEvent { const isImportEvent = (event: any): event is ImportEvent => 'integrationId' in event -const logger = buildLogger('app.dispatch') - export function integrationsServiceRouter() { const router = express.Router() diff --git a/packages/api/src/routers/svc/links.ts b/packages/api/src/routers/svc/links.ts index 4e6c23ed0..a769b6428 100644 --- a/packages/api/src/routers/svc/links.ts +++ b/packages/api/src/routers/svc/links.ts @@ -4,6 +4,7 @@ import express from 'express' import { readPushSubscription } from '../../datalayer/pubsub' import { createPageSaveRequest } from '../../services/create_page_save_request' +import { logger } from '../../utils/logger' interface CreateLinkRequestMessage { url: string @@ -14,9 +15,9 @@ export function linkServiceRouter() { const router = express.Router() router.post('/create', async (req, res) => { - console.log('create link req', req.query, req.body) + logger.info('create link req', req.query, req.body) const { message: msgStr, expired } = readPushSubscription(req) - console.log('read pubsub message', msgStr, 'has expired', expired) + logger.info('read pubsub message', msgStr, 'has expired', expired) if (!msgStr) { res.status(400).send('Bad Request') @@ -24,14 +25,14 @@ export function linkServiceRouter() { } if (expired) { - console.log('discarding expired message') + logger.info('discarding expired message') res.status(200).send('Expired') return } const data = JSON.parse(msgStr) if (!('url' in data) || !('userId' in data)) { - console.log('No file url or userId found in message') + logger.info('No file url or userId found in message') res.status(400).send('Bad Request') return } @@ -42,11 +43,11 @@ export function linkServiceRouter() { userId: msg.userId, url: msg.url, }) - console.log('create link request', request) + logger.info('create link request', request) res.status(200).send(request) } catch (err) { - console.log('create link failed', err) + logger.info('create link failed', err) res.status(500).send(err) } }) diff --git a/packages/api/src/routers/svc/newsletters.ts b/packages/api/src/routers/svc/newsletters.ts index 88538a967..4bf7d15ca 100644 --- a/packages/api/src/routers/svc/newsletters.ts +++ b/packages/api/src/routers/svc/newsletters.ts @@ -16,9 +16,7 @@ import { import { saveUrlFromEmail } from '../../services/save_url' import { getSubscriptionByNameAndUserId } from '../../services/subscriptions' import { isUrl } from '../../utils/helpers' -import { buildLogger } from '../../utils/logger' - -const logger = buildLogger('app.dispatch') +import { logger } from '../../utils/logger' interface SetConfirmationCodeMessage { emailAddress: string diff --git a/packages/api/src/routers/svc/reminders.ts b/packages/api/src/routers/svc/reminders.ts index 85f9ce6ea..390608376 100644 --- a/packages/api/src/routers/svc/reminders.ts +++ b/packages/api/src/routers/svc/reminders.ts @@ -12,6 +12,7 @@ import { initModels } from '../../server' import { getPagesWithReminder, PageReminder } from '../../services/reminders' import { getDeviceTokensByUserId } from '../../services/user_device_tokens' import { analytics } from '../../utils/analytics' +import { logger } from '../../utils/logger' import { sendEmail } from '../../utils/sendEmail' import { sendMulticastPushNotifications } from '../../utils/sendNotification' @@ -28,7 +29,7 @@ export function remindersServiceRouter() { // eslint-disable-next-line @typescript-eslint/no-misused-promises router.post('/trigger', async (req, res) => { - console.log('reminders/trigger') + logger.info('reminders/trigger') const { userId, scheduleTime } = req.body as { userId?: string @@ -56,7 +57,7 @@ export function remindersServiceRouter() { const user = await models.user.get(userId) if (!user || !user.email) { - console.log('user not found', userId) + logger.info('user not found', userId) res.status(400).send('User Not Found') return } @@ -64,12 +65,12 @@ export function remindersServiceRouter() { const pageReminders = await getPagesWithReminder(userId, remindAt) if (!pageReminders) { - console.log('pages with reminders not found', userId, scheduleTime) + logger.info('pages with reminders not found', userId, scheduleTime) res.status(200).send('Reminders Not Found') return } - console.log('page with reminders:', pageReminders) + logger.info('page with reminders:', pageReminders) const [pagesToNotify, pagesToUnarchive] = getPagesToNotifyAndUnarchive( pageReminders, @@ -81,7 +82,7 @@ export function remindersServiceRouter() { if (pagesToNotify.length > 0) { // we have configured Sendgrid to send a template if (!process.env.SENDGRID_REMINDER_TEMPLATE_ID) { - console.log('Sendgrid reminder email template_id not set') + logger.info('Sendgrid reminder email template_id not set') await updateRemindersStatus( models, @@ -99,7 +100,7 @@ export function remindersServiceRouter() { articles: pagesToNotify, } - console.log('dynamic template data:', dynamicTemplateData) + logger.info('dynamic template data:', dynamicTemplateData) await sendEmail({ from: env.sender.message, @@ -116,7 +117,7 @@ export function remindersServiceRouter() { } if (!deviceTokens) { - console.log('Device tokens not set:', userId) + logger.info('Device tokens not set:', userId) res.status(400).send('Device token Not Found') return @@ -126,7 +127,7 @@ export function remindersServiceRouter() { await updateRemindersStatus(models, userId, pagesToUnarchive, remindAt) res.status(200).send('Reminders triggered') } catch (e) { - console.log(e) + logger.info(e) res.status(500).send(e) } }) diff --git a/packages/api/src/routers/svc/rss_feed.ts b/packages/api/src/routers/svc/rss_feed.ts index e08eae4ca..72834d774 100644 --- a/packages/api/src/routers/svc/rss_feed.ts +++ b/packages/api/src/routers/svc/rss_feed.ts @@ -5,9 +5,7 @@ import { Subscription } from '../../entity/subscription' import { getRepository } from '../../entity/utils' import { SubscriptionStatus, SubscriptionType } from '../../generated/graphql' import { enqueueRssFeedFetch } from '../../utils/createTask' -import { buildLogger } from '../../utils/logger' - -const logger = buildLogger('app.dispatch') +import { logger } from '../../utils/logger' export function rssFeedRouter() { const router = express.Router() diff --git a/packages/api/src/routers/svc/upload.ts b/packages/api/src/routers/svc/upload.ts index 8ce2d9c51..7ed6ef910 100644 --- a/packages/api/src/routers/svc/upload.ts +++ b/packages/api/src/routers/svc/upload.ts @@ -2,14 +2,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import express from 'express' -import { readPushSubscription } from '../../datalayer/pubsub' -import { uploadToBucket } from '../../utils/uploads' -import { v4 as uuidv4 } from 'uuid' -import { env } from '../../env' import { DateTime } from 'luxon' -import { buildLogger } from '../../utils/logger' - -const logger = buildLogger('app.dispatch') +import { v4 as uuidv4 } from 'uuid' +import { readPushSubscription } from '../../datalayer/pubsub' +import { env } from '../../env' +import { logger } from '../../utils/logger' +import { uploadToBucket } from '../../utils/uploads' export function uploadServiceRouter() { const router = express.Router() diff --git a/packages/api/src/routers/svc/webhooks.ts b/packages/api/src/routers/svc/webhooks.ts index 5fda1fcd8..c2f9d6762 100644 --- a/packages/api/src/routers/svc/webhooks.ts +++ b/packages/api/src/routers/svc/webhooks.ts @@ -1,14 +1,12 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import axios, { Method } from 'axios' import express from 'express' import { readPushSubscription } from '../../datalayer/pubsub' import { getRepository } from '../../entity/utils' import { Webhook } from '../../entity/webhook' -import axios, { Method } from 'axios' -import { buildLogger } from '../../utils/logger' - -const logger = buildLogger('app.dispatch') +import { logger } from '../../utils/logger' export function webhooksServiceRouter() { const router = express.Router() @@ -68,14 +66,22 @@ export function webhooksServiceRouter() { }) logger.info('triggering webhook', url) - await axios.request({ - url, - method, - headers: { - 'Content-Type': webhook.contentType, - }, - data: body, - }) + try { + await axios.request({ + url, + method, + headers: { + 'Content-Type': webhook.contentType, + }, + data: body, + }) + } catch (error) { + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } + } } res.status(200).send('OK') diff --git a/packages/api/src/routers/text_to_speech.ts b/packages/api/src/routers/text_to_speech.ts index 4c86e7250..5fee7cc60 100644 --- a/packages/api/src/routers/text_to_speech.ts +++ b/packages/api/src/routers/text_to_speech.ts @@ -16,13 +16,11 @@ import { shouldSynthesize } from '../services/speech' import { getClaimsByToken } from '../utils/auth' import { corsConfig } from '../utils/corsConfig' import { enqueueTextToSpeech } from '../utils/createTask' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' const DEFAULT_VOICE = 'Larry' const DEFAULT_COMPLIMENTARY_VOICE = 'Evelyn' -const logger = buildLogger('app.dispatch') - export function textToSpeechRouter() { const router = express.Router() diff --git a/packages/api/src/routers/user_router.ts b/packages/api/src/routers/user_router.ts index a3ea891e3..d2feb11fe 100644 --- a/packages/api/src/routers/user_router.ts +++ b/packages/api/src/routers/user_router.ts @@ -7,11 +7,9 @@ import { getRepository } from '../entity/utils' import { env } from '../env' import { getClaimsByToken, getTokenByRequest } from '../utils/auth' import { corsConfig } from '../utils/corsConfig' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' import { sendEmail } from '../utils/sendEmail' -const logger = buildLogger('app.dispatch') - export function userRouter() { const router = express.Router() diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts index 3a936c60a..f1193f830 100755 --- a/packages/api/src/server.ts +++ b/packages/api/src/server.ts @@ -51,7 +51,11 @@ import { userRouter } from './routers/user_router' import { sentryConfig } from './sentry' import { getClaimsByToken, getTokenByRequest } from './utils/auth' import { corsConfig } from './utils/corsConfig' -import { buildLogger, buildLoggerTransport } from './utils/logger' +import { + buildLogger, + buildLoggerTransport, + CustomTypeOrmLogger, +} from './utils/logger' const PORT = process.env.PORT || 4000 @@ -80,6 +84,7 @@ export const AppDataSource = new DataSource({ entities: [__dirname + '/entity/**/*{.js,.ts}'], subscribers: [__dirname + '/events/**/*{.js,.ts}'], namingStrategy: new SnakeNamingStrategy(), + logger: new CustomTypeOrmLogger(), }) export const createApp = (): { diff --git a/packages/api/src/services/create_page_save_request.ts b/packages/api/src/services/create_page_save_request.ts index 5aa92f719..1a370f508 100644 --- a/packages/api/src/services/create_page_save_request.ts +++ b/packages/api/src/services/create_page_save_request.ts @@ -20,6 +20,7 @@ import { generateSlug, pageToArticleSavingRequest, } from '../utils/helpers' +import { logger } from '../utils/logger' interface PageSaveRequest { userId: string @@ -89,7 +90,7 @@ export const createPageSaveRequest = async ({ try { validateUrl(url) } catch (error) { - console.log('invalid url', url, error) + logger.info('invalid url', url, error) return Promise.reject({ errorCode: CreateArticleSavingRequestErrorCode.BadData, }) @@ -100,7 +101,7 @@ export const createPageSaveRequest = async ({ id: userId, }) if (!user) { - console.log('User not found', userId) + logger.info('User not found', userId) return Promise.reject({ errorCode: CreateArticleSavingRequestErrorCode.BadData, }) @@ -123,7 +124,7 @@ export const createPageSaveRequest = async ({ url, }) if (!page) { - console.log('Page not exists', url) + logger.info('Page not exists', url) page = { id: articleSavingRequestId, userId, @@ -145,7 +146,7 @@ export const createPageSaveRequest = async ({ // create processing page const pageId = await createPage(page, ctx) if (!pageId) { - console.log('Failed to create page', url) + logger.info('Failed to create page', url) return Promise.reject({ errorCode: CreateArticleSavingRequestErrorCode.BadData, }) diff --git a/packages/api/src/services/create_user.ts b/packages/api/src/services/create_user.ts index ec180f5be..e53f8a15a 100644 --- a/packages/api/src/services/create_user.ts +++ b/packages/api/src/services/create_user.ts @@ -8,6 +8,7 @@ import { getRepository } from '../entity/utils' import { SignupErrorCode } from '../generated/graphql' import { AuthProvider } from '../routers/auth/auth_types' import { AppDataSource } from '../server' +import { logger } from '../utils/logger' import { validateUsername } from '../utils/usernamePolicy' import { sendConfirmationEmail } from './send_emails' @@ -103,13 +104,13 @@ const validateInvite = async ( invite: Invite ): Promise => { if (invite.expirationTime < new Date()) { - console.log('rejecting invite, expired', invite) + logger.info('rejecting invite, expired', invite) return false } const membershipRepo = entityManager.getRepository(GroupMembership) const numMembers = await membershipRepo.countBy({ invite: { id: invite.id } }) if (numMembers >= invite.maxMembers) { - console.log('rejecting invite, too many users', invite, numMembers) + logger.info('rejecting invite, too many users', invite, numMembers) return false } return true diff --git a/packages/api/src/services/features.ts b/packages/api/src/services/features.ts index 99d53e194..ba6081f0e 100644 --- a/packages/api/src/services/features.ts +++ b/packages/api/src/services/features.ts @@ -1,8 +1,9 @@ +import * as jwt from 'jsonwebtoken' +import { IsNull, Not } from 'typeorm' import { Feature } from '../entity/feature' import { getRepository } from '../entity/utils' -import * as jwt from 'jsonwebtoken' import { env } from '../env' -import { IsNull, Not } from 'typeorm' +import { logger } from '../utils/logger' export enum FeatureName { UltraRealisticVoice = 'ultra-realistic-voice', @@ -33,7 +34,7 @@ const optInUltraRealisticVoice = async (uid: string): Promise => { }) if (feature) { // already opted in - console.log('already opted in') + logger.info('already opted in') return feature } @@ -45,7 +46,7 @@ const optInUltraRealisticVoice = async (uid: string): Promise => { let grantedAt: Date | null = new Date() if (count >= 1000) { - console.log('feature limit reached') + logger.info('feature limit reached') grantedAt = null } diff --git a/packages/api/src/services/integrations/pocket.ts b/packages/api/src/services/integrations/pocket.ts index 177428d73..dbbd36f1b 100644 --- a/packages/api/src/services/integrations/pocket.ts +++ b/packages/api/src/services/integrations/pocket.ts @@ -1,15 +1,13 @@ import axios from 'axios' import { ArticleSavingRequestStatus } from '../../elastic/types' import { env } from '../../env' -import { buildLogger } from '../../utils/logger' +import { logger } from '../../utils/logger' import { IntegrationService, RetrievedResult, RetrieveRequest, } from './integration' -const logger = buildLogger('app.dispatch') - interface PocketResponse { status: number // 1 if success complete: number // 1 if all items have been returned @@ -76,7 +74,11 @@ export class PocketIntegration extends IntegrationService { ) return response.data.access_token } catch (error) { - logger.error('error validating pocket token', error) + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } return null } } @@ -86,7 +88,7 @@ export class PocketIntegration extends IntegrationService { since: number, // unix timestamp in seconds count = 100, offset = 0 - ): Promise => { + ): Promise => { const url = `${this.POCKET_API_URL}/get` try { const response = await axios.post( @@ -108,8 +110,13 @@ export class PocketIntegration extends IntegrationService { return response.data } catch (error) { - logger.error('error retrieving pocket data', error) - throw new Error('Error retrieving pocket data') + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } + + return null } } @@ -125,6 +132,10 @@ export class PocketIntegration extends IntegrationService { count, offset ) + if (!pocketData) { + throw new Error('Error retrieving pocket data') + } + const pocketItems = Object.values(pocketData.list) const statusToState: Record = { '0': ArticleSavingRequestStatus.Succeeded, diff --git a/packages/api/src/services/integrations/readwise.ts b/packages/api/src/services/integrations/readwise.ts index a3bfe40ff..0ee052d5e 100644 --- a/packages/api/src/services/integrations/readwise.ts +++ b/packages/api/src/services/integrations/readwise.ts @@ -4,6 +4,7 @@ import { Integration } from '../../entity/integration' import { getRepository } from '../../entity/utils' import { env } from '../../env' import { wait } from '../../utils/helpers' +import { logger } from '../../utils/logger' import { getHighlightUrl } from '../highlights' import { IntegrationService } from './integration' @@ -48,7 +49,11 @@ export class ReadwiseIntegration extends IntegrationService { }) return response.status === 204 ? token : null } catch (error) { - console.error('error validating readwise token', error) + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } return null } } @@ -66,7 +71,7 @@ export class ReadwiseIntegration extends IntegrationService { // update integration syncedAt if successful if (result) { - console.info('updating integration syncedAt') + logger.info('updating integration syncedAt') await getRepository(Integration).update(integration.id, { syncedAt: new Date(), }) @@ -124,19 +129,21 @@ export class ReadwiseIntegration extends IntegrationService { ) return response.status === 200 } catch (error) { - if ( - axios.isAxiosError(error) && - error.response?.status === 429 && - retryCount < 3 - ) { - console.info('Readwise API rate limit exceeded, retrying...') - // wait for Retry-After seconds in the header if rate limited - // max retry count is 3 - const retryAfter = error.response?.headers['retry-after'] || '10' // default to 10 seconds - await wait(parseInt(retryAfter, 10) * 1000) - return this.syncWithReadwise(token, highlights, retryCount + 1) + if (axios.isAxiosError(error)) { + if (error.response?.status === 429 && retryCount < 3) { + logger.info('Readwise API rate limit exceeded, retrying...') + // wait for Retry-After seconds in the header if rate limited + // max retry count is 3 + const retryAfter = error.response?.headers['retry-after'] || '10' // default to 10 seconds + await wait(parseInt(retryAfter, 10) * 1000) + return this.syncWithReadwise(token, highlights, retryCount + 1) + } + + logger.error(error.response) + } else { + logger.error(error) } - console.error('Error creating highlights in Readwise', error) + return false } } diff --git a/packages/api/src/services/labels.ts b/packages/api/src/services/labels.ts index 6caa613e0..5837ddf72 100644 --- a/packages/api/src/services/labels.ts +++ b/packages/api/src/services/labels.ts @@ -8,6 +8,7 @@ import { User } from '../entity/user' import { getRepository } from '../entity/utils' import { CreateLabelInput } from '../generated/graphql' import { generateRandomColor } from '../utils/helpers' +import { logger } from '../utils/logger' const INTERNAL_LABELS_IN_LOWERCASE = [ 'newsletters', @@ -54,12 +55,12 @@ export const addLabelToPage = async ( let labelEntity = await getLabelByName(user.id, label.name) if (!labelEntity) { - console.log('creating new label', label.name) + logger.info('creating new label', label.name) labelEntity = await createLabel(user.id, label) } - console.log('adding label to page', label.name, pageId) + logger.info('adding label to page', label.name, pageId) return addLabelInPage( pageId, @@ -120,7 +121,7 @@ export const createLabels = async ( id: ctx.uid, }) if (!user) { - console.error('user not found') + logger.error('user not found') return [] } diff --git a/packages/api/src/services/popular_reads.ts b/packages/api/src/services/popular_reads.ts index e5086d120..7f79406d5 100644 --- a/packages/api/src/services/popular_reads.ts +++ b/packages/api/src/services/popular_reads.ts @@ -6,6 +6,7 @@ import { createPage } from '../elastic/pages' import { ArticleSavingRequestStatus, Page, PageContext } from '../elastic/types' import { PageType } from '../generated/graphql' import { generateSlug, stringToHash } from '../utils/helpers' +import { logger } from '../utils/logger' type PopularRead = { url: string @@ -51,7 +52,7 @@ const popularRead = (key: string): PopularRead | undefined => { originalHtml, } } catch (e) { - console.log('error adding popular read', e) + logger.info('error adding popular read', e) return undefined } } diff --git a/packages/api/src/services/reminders.ts b/packages/api/src/services/reminders.ts index d767ec5f6..d94e065a4 100644 --- a/packages/api/src/services/reminders.ts +++ b/packages/api/src/services/reminders.ts @@ -1,7 +1,8 @@ -import { getRepository } from '../entity/utils' -import { Reminder } from '../entity/reminder' -import { getPageById } from '../elastic/pages' import { IsNull, Not } from 'typeorm' +import { getPageById } from '../elastic/pages' +import { Reminder } from '../entity/reminder' +import { getRepository } from '../entity/utils' +import { logger } from '../utils/logger' export interface PageReminder { pageId: string @@ -31,7 +32,7 @@ export const getPagesWithReminder = async ( if (reminder.elasticPageId) { const page = await getPageById(reminder.elasticPageId) if (!page) { - console.log(`Reminder ${reminder.id} has invalid elasticPageId`) + logger.info(`Reminder ${reminder.id} has invalid elasticPageId`) continue } diff --git a/packages/api/src/services/reports.ts b/packages/api/src/services/reports.ts index e83b0f70f..1495af024 100644 --- a/packages/api/src/services/reports.ts +++ b/packages/api/src/services/reports.ts @@ -1,8 +1,9 @@ -import { ReportItemInput, ReportType } from '../generated/graphql' -import { ContentDisplayReport } from '../entity/reports/content_display_report' -import { AbuseReport } from '../entity/reports/abuse_report' import { getPageById } from '../elastic/pages' +import { AbuseReport } from '../entity/reports/abuse_report' +import { ContentDisplayReport } from '../entity/reports/content_display_report' import { getRepository } from '../entity/utils' +import { ReportItemInput, ReportType } from '../generated/graphql' +import { logger } from '../utils/logger' export const saveContentDisplayReport = async ( uid: string, @@ -13,7 +14,7 @@ export const saveContentDisplayReport = async ( const page = await getPageById(input.pageId) if (!page) { - console.log('unable to submit report, page not found', input) + logger.info('unable to submit report, page not found', input) return false } @@ -41,12 +42,12 @@ export const saveAbuseReport = async ( const page = await getPageById(input.pageId) if (!page) { - console.log('unable to submit report, page not found', input) + logger.info('unable to submit report, page not found', input) return false } if (!input.sharedBy) { - console.log('unable to submit report, sharedBy not found', input) + logger.info('unable to submit report, sharedBy not found', input) return false } diff --git a/packages/api/src/services/save_email.ts b/packages/api/src/services/save_email.ts index 50da3504d..bb1b2db6b 100644 --- a/packages/api/src/services/save_email.ts +++ b/packages/api/src/services/save_email.ts @@ -9,15 +9,13 @@ import { validatedDate, wordsCount, } from '../utils/helpers' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' import { FAKE_URL_PREFIX, parsePreparedContent, parseUrlMetadata, } from '../utils/parser' -const logger = buildLogger('app.dispatch') - export type SaveContext = { pubsub: PubsubClient uid: string diff --git a/packages/api/src/services/save_file.ts b/packages/api/src/services/save_file.ts index e7624b786..235670839 100644 --- a/packages/api/src/services/save_file.ts +++ b/packages/api/src/services/save_file.ts @@ -1,6 +1,7 @@ import { Knex } from 'knex' import { PubsubClient } from '../datalayer/pubsub' import { UserData } from '../datalayer/user/model' +import { updatePage } from '../elastic/pages' import { homePageURL } from '../env' import { ArticleSavingRequestStatus, @@ -9,9 +10,9 @@ import { SaveResult, } from '../generated/graphql' import { DataModels } from '../resolvers/types' -import { createLabels } from './labels' -import { updatePage } from '../elastic/pages' +import { logger } from '../utils/logger' import { getStorageFileDetails } from '../utils/uploads' +import { createLabels } from './labels' type SaveContext = { pubsub: PubsubClient @@ -28,7 +29,7 @@ export const saveFile = async ( saver: UserData, input: SaveFileInput ): Promise => { - console.log('saving file with input', input) + logger.info('saving file with input', input) const pageId = input.clientRequestId const uploadFile = await ctx.models.uploadFile.getWhere({ id: input.uploadFileId, @@ -70,7 +71,7 @@ export const saveFile = async ( ctx ) if (!updated) { - console.log('error updating page', pageId) + logger.info('error updating page', pageId) return { errorCodes: [SaveErrorCode.Unknown], } diff --git a/packages/api/src/services/save_newsletter_email.ts b/packages/api/src/services/save_newsletter_email.ts index 5e5284915..709c071cf 100644 --- a/packages/api/src/services/save_newsletter_email.ts +++ b/packages/api/src/services/save_newsletter_email.ts @@ -8,8 +8,8 @@ import { env } from '../env' import { ContentReader } from '../generated/graphql' import { analytics } from '../utils/analytics' import { isBase64Image } from '../utils/helpers' +import { logger } from '../utils/logger' import { fetchFavicon } from '../utils/parser' -import { sendMulticastPushNotifications } from '../utils/sendNotification' import { addLabelToPage } from './labels' import { SaveContext, saveEmail, SaveEmailInput } from './save_email' import { saveSubscription } from './subscriptions' @@ -46,7 +46,7 @@ export const saveNewsletterEmail = async ( }) if (!data.content) { - console.log('newsletter not created, no content:', data.email) + logger.info('newsletter not created, no content:', data.email) return false } @@ -64,7 +64,7 @@ export const saveNewsletterEmail = async ( } const page = await saveEmail(saveCtx, input) if (!page) { - console.log('newsletter not created:', input.title) + logger.info('newsletter not created:', input.title) return false } @@ -86,19 +86,19 @@ export const saveNewsletterEmail = async ( unsubscribeHttpUrl: data.unsubHttpUrl, icon, }) - console.log('subscription saved', subscriptionId) + logger.info('subscription saved', subscriptionId) // adds newsletters label to page const result = await addLabelToPage(saveCtx, page.id, { name: 'Newsletter', color: '#07D2D1', }) - console.log('newsletter label added:', result) + logger.info('newsletter label added:', result) // sends push notification const deviceTokens = await getDeviceTokensByUserId(newsletterEmail.user.id) if (!deviceTokens) { - console.log('Device tokens not set:', newsletterEmail.user.id) + logger.info('Device tokens not set:', newsletterEmail.user.id) return true } diff --git a/packages/api/src/services/save_page.ts b/packages/api/src/services/save_page.ts index 518e0d8c8..0d0ab5fbf 100644 --- a/packages/api/src/services/save_page.ts +++ b/packages/api/src/services/save_page.ts @@ -22,13 +22,11 @@ import { validatedDate, wordsCount, } from '../utils/helpers' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' import { parsePreparedContent } from '../utils/parser' import { createPageSaveRequest } from './create_page_save_request' import { createLabels } from './labels' -const logger = buildLogger('app.dispatch') - type SaveContext = { pubsub: PubsubClient models: DataModels diff --git a/packages/api/src/services/save_url.ts b/packages/api/src/services/save_url.ts index cba500588..2f3f7ac54 100644 --- a/packages/api/src/services/save_url.ts +++ b/packages/api/src/services/save_url.ts @@ -4,6 +4,7 @@ import { User } from '../entity/user' import { getRepository } from '../entity/utils' import { homePageURL } from '../env' import { SaveErrorCode, SaveResult, SaveUrlInput } from '../generated/graphql' +import { logger } from '../utils/logger' import { createPageSaveRequest } from './create_page_save_request' import { createLabels } from './labels' @@ -45,7 +46,7 @@ export const saveUrl = async ( }`, } } catch (error) { - console.log('error enqueuing request', error) + logger.info('error enqueuing request', error) return { __typename: 'SaveError', errorCodes: [SaveErrorCode.Unknown], diff --git a/packages/api/src/services/subscriptions.ts b/packages/api/src/services/subscriptions.ts index d2063b312..1db7bbe6e 100644 --- a/packages/api/src/services/subscriptions.ts +++ b/packages/api/src/services/subscriptions.ts @@ -3,12 +3,10 @@ import { NewsletterEmail } from '../entity/newsletter_email' import { Subscription } from '../entity/subscription' import { getRepository } from '../entity/utils' import { SubscriptionStatus, SubscriptionType } from '../generated/graphql' -import { buildLogger } from '../utils/logger' +import { logger } from '../utils/logger' import { sendEmail } from '../utils/sendEmail' import { createNewsletterEmail } from './newsletters' -const logger = buildLogger('app.dispatch') - interface SaveSubscriptionInput { userId: string name: string diff --git a/packages/api/src/utils/auth.ts b/packages/api/src/utils/auth.ts index 45ed00b39..dbb94c46d 100644 --- a/packages/api/src/utils/auth.ts +++ b/packages/api/src/utils/auth.ts @@ -8,6 +8,7 @@ import { ApiKey } from '../entity/api_key' import { getRepository } from '../entity/utils' import { env } from '../env' import { Claims, ClaimsToSet } from '../resolvers/types' +import { logger } from './logger' export const OmnivoreAuthorizationHeader = 'Omnivore-Authorization' @@ -81,7 +82,7 @@ export const getClaimsByToken = async ( e instanceof jwt.JsonWebTokenError && !(e instanceof jwt.TokenExpiredError) ) { - console.log(`not a jwt token, checking api key`, { token }) + logger.info(`not a jwt token, checking api key`, { token }) return claimsFromApiKey(token) } diff --git a/packages/api/src/utils/createTask.ts b/packages/api/src/utils/createTask.ts index f6d72bcb7..9f89cc70f 100644 --- a/packages/api/src/utils/createTask.ts +++ b/packages/api/src/utils/createTask.ts @@ -15,14 +15,20 @@ import { import { signFeatureToken } from '../services/features' import { generateVerificationToken, OmnivoreAuthorizationHeader } from './auth' import { CreateTaskError } from './errors' -import { buildLogger } from './logger' +import { logger } from './logger' import View = google.cloud.tasks.v2.Task.View -const logger = buildLogger('app.dispatch') - // Instantiates a client. const client = new CloudTasksClient() +const logError = (error: Error): void => { + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } +} + const createHttpTaskWithToken = async ({ project = process.env.GOOGLE_CLOUD_PROJECT, queue = env.queue.name, @@ -245,8 +251,8 @@ export const enqueueParseRequest = async ({ // Calling the handler function directly. setTimeout(() => { axios.post(env.queue.contentFetchUrl, payload).catch((error) => { - console.error(error) - console.error( + logError(error) + logger.warning( `Error occurred while requesting local puppeteer-parse function\nPlease, ensure your function is set up properly and running using "yarn start" from the "/pkg/gcf/puppeteer-parse" folder` ) }) @@ -399,7 +405,7 @@ export const enqueueTextToSpeech = async ({ // Calling the handler function directly. setTimeout(() => { axios.post(taskHandlerUrl, payload).catch((error) => { - console.error(error) + logError(error) }) }, 0) return '' @@ -450,7 +456,7 @@ export const enqueueRecommendation = async ( headers, }) .catch((error) => { - console.error(error) + logError(error) }) }, 0) return '' @@ -494,7 +500,7 @@ export const enqueueImportFromIntegration = async ( headers, }) .catch((error) => { - console.error(error) + logError(error) }) }, 0) return nanoid() @@ -543,7 +549,7 @@ export const enqueueThumbnailTask = async ( headers, }) .catch((error) => { - console.error(error) + logError(error) }) }, 0) return '' @@ -590,7 +596,7 @@ export const enqueueRssFeedFetch = async ( headers, }) .catch((error) => { - console.error(error) + logError(error) }) }, 0) return nanoid() diff --git a/packages/api/src/utils/helpers.ts b/packages/api/src/utils/helpers.ts index daf305881..789912303 100644 --- a/packages/api/src/utils/helpers.ts +++ b/packages/api/src/utils/helpers.ts @@ -20,7 +20,7 @@ import { CreateArticlesSuccessPartial } from '../resolvers' import { Claims, WithDataSourcesContext } from '../resolvers/types' import { validateUrl } from '../services/create_page_save_request' import { Merge } from '../util' - +import { logger } from './logger' interface InputObject { // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any @@ -225,7 +225,7 @@ export const validatedDate = ( } return new Date(date) } catch (e) { - console.log('error validating date', date, e) + logger.error('error validating date', date, e) return undefined } } @@ -247,7 +247,7 @@ export const titleForFilePath = (url: string): string => { const title = decodeURI(path.basename(new URL(url).pathname, '.pdf')) return title } catch (e) { - console.log(e) + logger.error(e) } return url } @@ -299,7 +299,7 @@ export const isUrl = (str: string): boolean => { validateUrl(str) return true } catch { - console.log('not an url', str) + logger.error('not an url', str) return false } } diff --git a/packages/api/src/utils/highlightGenerator.ts b/packages/api/src/utils/highlightGenerator.ts index 02da8695d..bfdd346c7 100644 --- a/packages/api/src/utils/highlightGenerator.ts +++ b/packages/api/src/utils/highlightGenerator.ts @@ -3,9 +3,7 @@ import { parseHTML } from 'linkedom' import { nanoid } from 'nanoid' import { v4 as uuidv4 } from 'uuid' import { interpolationSearch } from './interpolationSearch' -import { buildLogger } from './logger' - -const logger = buildLogger('app.dispatch') +import { logger } from './logger' const highlightTag = 'omnivore_highlight' export const maxHighlightLength = 2000 diff --git a/packages/api/src/utils/logger.ts b/packages/api/src/utils/logger.ts index 2daadb5bf..f87204af8 100644 --- a/packages/api/src/utils/logger.ts +++ b/packages/api/src/utils/logger.ts @@ -3,6 +3,12 @@ import { LoggingWinston } from '@google-cloud/logging-winston' import { cloneDeep, isArray, isObject, isString, truncate } from 'lodash' import { DateTime } from 'luxon' +import { + AdvancedConsoleLogger, + Logger as TypeOrmLogger, + LoggerOptions as TypeOrmLoggerOptions, + QueryRunner, +} from 'typeorm' import { config, format, @@ -15,6 +21,30 @@ import TransportStream from 'winston-transport' import { ConsoleTransportOptions } from 'winston/lib/winston/transports' import { env } from '../env' +export class CustomTypeOrmLogger + extends AdvancedConsoleLogger + implements TypeOrmLogger +{ + private logger: Logger + + constructor(options?: TypeOrmLoggerOptions) { + super(options) + this.logger = buildLogger('typeorm') + } + + logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) { + this.logger.debug(query, parameters, queryRunner) + } + + log( + level: 'log' | 'info' | 'warn', + message: any, + queryRunner?: QueryRunner + ): void { + this.logger.log(level, message, queryRunner) + } +} + const colors = { emerg: 'inverse underline magenta', alert: 'underline magenta', @@ -125,4 +155,6 @@ export interface LogRecord { [key: string]: any } +export const logger = buildLogger('app') + export default {} diff --git a/packages/api/src/utils/parser.ts b/packages/api/src/utils/parser.ts index 841e82085..77cf29302 100644 --- a/packages/api/src/utils/parser.ts +++ b/packages/api/src/utils/parser.ts @@ -454,8 +454,12 @@ export const parseUrlMetadata = async ( try { const res = await axios.get(url) return parsePageMetadata(res.data) - } catch (e) { - logger.info('failed to get:', url, e) + } catch (error) { + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } return undefined } } @@ -500,7 +504,11 @@ export const fetchFavicon = async ( const domain = new URL(realUrl).hostname return `https://api.faviconkit.com/${domain}/128` } catch (e) { - console.log('Error fetching favicon', e) + if (axios.isAxiosError(e)) { + logger.error('failed to get favicon:', e.response?.status) + } else { + logger.error('failed to get favicon:', e) + } return undefined } } @@ -689,8 +697,12 @@ export const getDistillerResult = async ( timeout: 5000, }) return response.data - } catch (e) { - logger.info('Error parsing by distiller', e) + } catch (error) { + if (axios.isAxiosError(error)) { + logger.error(error.response) + } else { + logger.error(error) + } return undefined } } diff --git a/packages/api/src/utils/sendEmail.ts b/packages/api/src/utils/sendEmail.ts index 16ffaebb8..b8cee8bea 100644 --- a/packages/api/src/utils/sendEmail.ts +++ b/packages/api/src/utils/sendEmail.ts @@ -1,6 +1,7 @@ -import { MailService } from '@sendgrid/mail' import { MailDataRequired } from '@sendgrid/helpers/classes/mail' +import { MailService } from '@sendgrid/mail' import { env } from '../env' +import { logger } from './logger' type SendGridResponse = { body?: string @@ -25,7 +26,7 @@ export const sendEmail = async (msg: MailDataRequired): Promise => { const client = new MailService() if (!process.env.SENDGRID_MSGS_API_KEY) { if (env.dev.isLocal) { - console.log('SendGrid API key not set.\nSending email:', msg) + logger.warning('SendGrid API key not set.\nSending email:', msg) return true } @@ -34,18 +35,19 @@ export const sendEmail = async (msg: MailDataRequired): Promise => { client.setApiKey(process.env.SENDGRID_MSGS_API_KEY) - console.log('sending email', msg) + logger.info('sending email', msg) try { const response = await client.send(msg) - console.log('email sent', response) + logger.info('email sent', response) return true } catch (error) { - console.log('error sending email', error) const err = asSendGridError(error) if (err) { - console.log('sendgrid error:', JSON.stringify(err.response?.body)) + logger.error('sendgrid error:', JSON.stringify(err.response?.body)) + } else { + logger.error('error sending email', error) } return false diff --git a/packages/api/src/utils/sendNotification.ts b/packages/api/src/utils/sendNotification.ts index 0421b5688..f81c3920d 100644 --- a/packages/api/src/utils/sendNotification.ts +++ b/packages/api/src/utils/sendNotification.ts @@ -7,6 +7,7 @@ import { } from 'firebase-admin/messaging' import { env } from '../env' import { analytics } from './analytics' +import { logger } from './logger' export type PushNotificationType = 'newsletter' | 'reminder' | 'rule' @@ -32,11 +33,11 @@ export const sendPushNotification = async ( }) const res = await getMessaging().send(message) - console.log(res) + logger.info(res) return res } catch (err) { - console.log('firebase cloud message error: ', err) + logger.error('firebase cloud message error: ', err) return undefined } @@ -58,13 +59,13 @@ export const sendMulticastPushNotifications = async ( }, }) - console.log('sending multicast message: ', JSON.stringify(message)) + logger.info('sending multicast message: ', JSON.stringify(message)) const res = await getMessaging().sendMulticast(message) - console.log('send notification result: ', JSON.stringify(res.responses)) + logger.info('send notification result: ', JSON.stringify(res.responses)) return res } catch (err) { - console.log('firebase cloud message error: ', err) + logger.error('firebase cloud message error: ', err) return undefined } @@ -75,11 +76,11 @@ export const sendBatchPushNotifications = async ( ): Promise => { try { const res = await getMessaging().sendAll(messages) - console.log('success count: ', res.successCount) + logger.info('success count: ', res.successCount) return res } catch (err) { - console.log('firebase cloud message error: ', err) + logger.error('firebase cloud message error: ', err) return undefined } diff --git a/packages/api/src/utils/uploads.ts b/packages/api/src/utils/uploads.ts index eeb1dfa0d..fbf86b898 100644 --- a/packages/api/src/utils/uploads.ts +++ b/packages/api/src/utils/uploads.ts @@ -3,6 +3,7 @@ import { File, GetSignedUrlConfig, Storage } from '@google-cloud/storage' import { env } from '../env' import { ContentReader, PageType } from '../generated/graphql' +import { logger } from './logger' export const contentReaderForPage = ( pageType: PageType, @@ -57,7 +58,7 @@ export const generateUploadSignedUrl = async ( expires: Date.now() + 15 * 60 * 1000, // 15 minutes contentType: contentType, } - console.log('signed url for: ', options) + logger.info('signed url for: ', options) // Get a v4 signed URL for uploading file const [url] = await storage @@ -79,7 +80,7 @@ export const generateDownloadSignedUrl = async ( .bucket(bucketName) .file(filePathName) .getSignedUrl(options) - console.log('generating download signed url', url) + logger.info('generating download signed url', url) return url }