From 3912600795c59beff12c44b5b012e2695e9b4dd8 Mon Sep 17 00:00:00 2001 From: Qing Date: Wed, 24 Aug 2022 22:20:26 +0800 Subject: [PATCH 1/3] fix: add axiom logs --- apps/main/next.config.js | 2 + apps/main/package.json | 1 + apps/main/src/middleware.ts | 31 +-- apps/main/src/pages/_app.tsx | 20 +- apps/main/src/pages/api/auth/[...nextauth].ts | 177 +++++++++--------- .../src/pages/api/auth/delete/facebook.ts | 3 +- .../api/content-classifier/toxic-text.ts | 4 +- .../src/pages/api/mutation-event/index.ts | 4 +- .../pages/api/notification/register-device.ts | 4 +- apps/main/src/pages/api/page.ts | 3 +- apps/main/src/server/common/api-handler.ts | 7 +- .../notification/push-web-notification.ts | 14 +- .../send-notification-via-email.ts | 4 +- .../src/server/services/notification/send.ts | 4 +- apps/main/src/server/services/user.ts | 3 +- pnpm-lock.yaml | 13 +- 16 files changed, 154 insertions(+), 140 deletions(-) diff --git a/apps/main/next.config.js b/apps/main/next.config.js index 10d7d099f..efa86f04c 100644 --- a/apps/main/next.config.js +++ b/apps/main/next.config.js @@ -5,6 +5,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({ }); const { withPlausibleProxy } = require('next-plausible'); const { RelativeCiAgentWebpackPlugin } = require('@relative-ci/agent'); +const { withAxiom } = require('next-axiom'); const analyticsDomain = process.env.NEXT_PUBLIC_ANALYTICS_DOMAIN; const isProd = process.env.NODE_ENV === 'production'; @@ -14,6 +15,7 @@ const isProd = process.env.NODE_ENV === 'production'; **/ module.exports = withPlugins( [ + [withAxiom], [withBundleAnalyzer], [ withPlausibleProxy({ diff --git a/apps/main/package.json b/apps/main/package.json index 1daf88a0a..48cde2f24 100644 --- a/apps/main/package.json +++ b/apps/main/package.json @@ -58,6 +58,7 @@ "lodash": "4.17.21", "next": "12.2.5", "next-auth": "4.10.3", + "next-axiom": "^0.13.0", "next-compose-plugins": "2.2.1", "next-connect": "0.12.2", "next-mdx-remote": "3.0.8", diff --git a/apps/main/src/middleware.ts b/apps/main/src/middleware.ts index 80568c2c1..1db5b98b3 100644 --- a/apps/main/src/middleware.ts +++ b/apps/main/src/middleware.ts @@ -1,20 +1,23 @@ import { withAuth } from 'next-auth/middleware'; +import { withAxiom } from 'next-axiom'; -export default withAuth({ - callbacks: { - authorized: ({ token, req }) => { - if (req.nextUrl.pathname.includes('/api/mutation-event')) { - // We add a custom secret to verify the hasura event callback requests - return ( - req.headers.get('hasura_event_secret') === - process.env.HASURA_EVENT_SECRET - ); - } - // Anonymous user doesn't have an email address - return !!(token?.email || token?.name); +export default withAxiom( + withAuth({ + callbacks: { + authorized: ({ token, req }) => { + if (req.nextUrl.pathname.includes('/api/mutation-event')) { + // We add a custom secret to verify the hasura event callback requests + return ( + req.headers.get('hasura_event_secret') === + process.env.HASURA_EVENT_SECRET + ); + } + // Anonymous user doesn't have an email address + return !!(token?.email || token?.name); + }, }, - }, -}); + }), +); export const config = { matcher: [ diff --git a/apps/main/src/pages/_app.tsx b/apps/main/src/pages/_app.tsx index 7bb3a396a..8652573da 100644 --- a/apps/main/src/pages/_app.tsx +++ b/apps/main/src/pages/_app.tsx @@ -3,7 +3,6 @@ import { SessionProvider } from 'next-auth/react'; import PlausibleProvider from 'next-plausible'; import { ThemeProvider as NextThemesProvider } from 'next-themes'; import type { AppProps } from 'next/app'; -import { NextWebVitalsMetric } from 'next/app'; import * as React from 'react'; import { ToastProvider } from '$/components/toast'; @@ -51,24 +50,7 @@ function App({ export default App; -export function reportWebVitals(metric: NextWebVitalsMetric) { - const url = process.env.NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT; - - if (!url) { - return; - } - - const body = JSON.stringify({ - route: window.__NEXT_DATA__.page, - ...metric, - }); - - if (navigator.sendBeacon) { - navigator.sendBeacon(url, body); - } else { - fetch(url, { body, method: 'POST', keepalive: true }); - } -} +export { reportWebVitals } from 'next-axiom'; export const loadFeatures = () => import( diff --git a/apps/main/src/pages/api/auth/[...nextauth].ts b/apps/main/src/pages/api/auth/[...nextauth].ts index 39c13ba24..5843bb86f 100644 --- a/apps/main/src/pages/api/auth/[...nextauth].ts +++ b/apps/main/src/pages/api/auth/[...nextauth].ts @@ -1,4 +1,5 @@ import NextAuth from 'next-auth'; +import { withAxiom } from 'next-axiom'; import { HASURA_TOKEN_MAX_AGE, SESSION_MAX_AGE } from '$/lib/constants'; import { query } from '$/server/common/gql'; @@ -10,94 +11,96 @@ import { createAuthToken } from '$/server/utilities/create-token'; import { defaultCookies } from '$/server/utilities/default-cookies'; import { isENVDev } from '$/server/utilities/env'; -export default NextAuth({ - providers: authProviders, - session: { - strategy: 'jwt', - maxAge: SESSION_MAX_AGE, - }, - pages: { - signIn: '/auth/sign-in', - newUser: '/auth/welcome?isNewUser=true', // New users will be directed here on first sign in - error: '/auth/sign-in', // Error code passed in query string as ?error= - verifyRequest: '/auth/verify-request', - }, - callbacks: { - /** - * @param token Decrypted JSON Web Token - * @param user User object (only available on sign in) - * @param account Provider account (only available on sign in) - * @param profile Provider profile (only available on sign in) - * @return JSON Web Token that will be saved - */ - async jwt({ token /* user account, profile */ }) { - // TODO: Ask user to fill these fields, don't fill them automatically - //if (user) { - // await fillUserFields( - // user as any, - // profile as any, - // account?.provider as any, - // ); - //} - return { - ...token, - }; +export default withAxiom( + NextAuth({ + providers: authProviders, + session: { + strategy: 'jwt', + maxAge: SESSION_MAX_AGE, }, - async session({ session, token }) { - const userId = token.sub; - if (!userId) { - throw new Error(`Expect valid user id`); - } - const projects = await query( - UserProjectsDocument, - { - userId, - }, - 'projects', - ); - const editableProjectIds = - projects.map(({ id }: { id: string }) => id) || []; - session.hasuraToken = createAuthToken( - { - userId: userId, - name: token.name || '', - email: token.email || '', - }, - { - maxAge: HASURA_TOKEN_MAX_AGE, - allowedRoles: ['user'], - defaultRole: 'user', - role: 'user', - }, - ); - if (session.user) { - session.user.id = userId; - session.user.editableProjectIds = editableProjectIds; - } - return session; + pages: { + signIn: '/auth/sign-in', + newUser: '/auth/welcome?isNewUser=true', // New users will be directed here on first sign in + error: '/auth/sign-in', // Error code passed in query string as ?error= + verifyRequest: '/auth/verify-request', }, - async signIn({ account, profile }) { - // Restrict access to people with verified accounts - if (account.provider === 'google' && profile.verified_email !== true) { - return false; - } - return true; + callbacks: { + /** + * @param token Decrypted JSON Web Token + * @param user User object (only available on sign in) + * @param account Provider account (only available on sign in) + * @param profile Provider profile (only available on sign in) + * @return JSON Web Token that will be saved + */ + async jwt({ token /* user account, profile */ }) { + // TODO: Ask user to fill these fields, don't fill them automatically + //if (user) { + // await fillUserFields( + // user as any, + // profile as any, + // account?.provider as any, + // ); + //} + return { + ...token, + }; + }, + async session({ session, token }) { + const userId = token.sub; + if (!userId) { + throw new Error(`Expect valid user id`); + } + const projects = await query( + UserProjectsDocument, + { + userId, + }, + 'projects', + ); + const editableProjectIds = + projects.map(({ id }: { id: string }) => id) || []; + session.hasuraToken = createAuthToken( + { + userId: userId, + name: token.name || '', + email: token.email || '', + }, + { + maxAge: HASURA_TOKEN_MAX_AGE, + allowedRoles: ['user'], + defaultRole: 'user', + role: 'user', + }, + ); + if (session.user) { + session.user.id = userId; + session.user.editableProjectIds = editableProjectIds; + } + return session; + }, + async signIn({ account, profile }) { + // Restrict access to people with verified accounts + if (account.provider === 'google' && profile.verified_email !== true) { + return false; + } + return true; + }, }, - }, - events: { - async createUser({ user }) { - if (!user.email) { - return console.info('Create an anonymous user'); - } - await sendWelcomeLetter({ - to: { - name: user.name || user.email, - email: user.email, - }, - }); + events: { + async createUser({ user }) { + if (!user.email) { + return console.info('Create an anonymous user'); + } + await sendWelcomeLetter({ + to: { + name: user.name || user.email, + email: user.email, + }, + }); + }, }, - }, - cookies: defaultCookies(process.env.NEXTAUTH_URL.startsWith('https://')), - adapter: nextAuthAdapter(), - debug: isENVDev, -}); + cookies: defaultCookies(process.env.NEXTAUTH_URL.startsWith('https://')), + adapter: nextAuthAdapter(), + debug: isENVDev, + }), +); diff --git a/apps/main/src/pages/api/auth/delete/facebook.ts b/apps/main/src/pages/api/auth/delete/facebook.ts index f241e9391..29ad4c184 100644 --- a/apps/main/src/pages/api/auth/delete/facebook.ts +++ b/apps/main/src/pages/api/auth/delete/facebook.ts @@ -1,5 +1,6 @@ import crypto from 'crypto'; import { NextApiRequest, NextApiResponse } from 'next'; +import { withAxiom } from 'next-axiom'; import { getApiHandler } from '$/server/common/api-handler'; import { ApiError } from '$/server/common/error'; @@ -94,4 +95,4 @@ function unescape(str: string) { .replace(/_/g, '/'); } -export default handler; +export default withAxiom(handler); diff --git a/apps/main/src/pages/api/content-classifier/toxic-text.ts b/apps/main/src/pages/api/content-classifier/toxic-text.ts index e2f47d3e5..7cb1e9771 100644 --- a/apps/main/src/pages/api/content-classifier/toxic-text.ts +++ b/apps/main/src/pages/api/content-classifier/toxic-text.ts @@ -1,7 +1,9 @@ +import { withAxiom } from 'next-axiom'; + import { getApiHandler } from '$/server/common/api-handler'; import { checkToxicText } from '$/server/services/content-classifier/toxic-text'; const handler = getApiHandler(); handler.get(checkToxicText); -export default handler; +export default withAxiom(handler); diff --git a/apps/main/src/pages/api/mutation-event/index.ts b/apps/main/src/pages/api/mutation-event/index.ts index f96b72519..1cb9dd1a1 100644 --- a/apps/main/src/pages/api/mutation-event/index.ts +++ b/apps/main/src/pages/api/mutation-event/index.ts @@ -1,3 +1,5 @@ +import { withAxiom } from 'next-axiom'; + import { getApiHandler } from '$/server/common/api-handler'; import { handleMutationEvent } from '$/server/services/mutation-event/mutation-event'; @@ -5,4 +7,4 @@ const handler = getApiHandler(); handler.post(handleMutationEvent); -export default handler; +export default withAxiom(handler); diff --git a/apps/main/src/pages/api/notification/register-device.ts b/apps/main/src/pages/api/notification/register-device.ts index 52d82e4c3..ed26e4542 100644 --- a/apps/main/src/pages/api/notification/register-device.ts +++ b/apps/main/src/pages/api/notification/register-device.ts @@ -1,7 +1,9 @@ +import { withAxiom } from 'next-axiom'; + import { getApiHandler } from '$/server/common/api-handler'; import { registerDevice } from '$/server/services/notification/register'; const handler = getApiHandler(); handler.post(registerDevice); -export default handler; +export default withAxiom(handler); diff --git a/apps/main/src/pages/api/page.ts b/apps/main/src/pages/api/page.ts index 5e749725d..b6f28240f 100644 --- a/apps/main/src/pages/api/page.ts +++ b/apps/main/src/pages/api/page.ts @@ -1,4 +1,5 @@ import Cors from 'cors'; +import { withAxiom } from 'next-axiom'; import { getApiHandler } from '$/server/common/api-handler'; import { getPage } from '$/server/services/page'; @@ -10,4 +11,4 @@ const handler = getApiHandler(); handler.use(cors); handler.get(getPage); -export default handler; +export default withAxiom(handler); diff --git a/apps/main/src/server/common/api-handler.ts b/apps/main/src/server/common/api-handler.ts index 791bb5950..0d94db745 100644 --- a/apps/main/src/server/common/api-handler.ts +++ b/apps/main/src/server/common/api-handler.ts @@ -1,4 +1,5 @@ import { NextApiRequest, NextApiResponse } from 'next'; +import { log } from 'next-axiom'; import connect, { ErrorHandler } from 'next-connect'; import { ApiError } from './error'; @@ -7,9 +8,9 @@ export const handleInternalFailure: ErrorHandler< NextApiRequest, NextApiResponse > = (error, req, res) => { - console.error('internal error', error); - console.error('query', req.query); - console.error('body', req.body); + log.error('internal error', error); + log.error('query', req.query); + log.error('body', req.body); if (error instanceof ApiError) { return res.status(error.httpStatus).send(error.message); diff --git a/apps/main/src/server/services/notification/push-web-notification.ts b/apps/main/src/server/services/notification/push-web-notification.ts index 060969b42..e1cef4a11 100644 --- a/apps/main/src/server/services/notification/push-web-notification.ts +++ b/apps/main/src/server/services/notification/push-web-notification.ts @@ -1,3 +1,4 @@ +import { log } from 'next-axiom'; import webpush from 'web-push'; import { NotificationType_Enum } from '$/graphql/generated/types'; @@ -25,14 +26,13 @@ export function pushWebNotification(payload: NotificationPayload) { WEB_PUSH_OPTIONS, ); } catch (error) { - console.error('webpush error', JSON.stringify(error, null, 2)); const err: any = error; if ( err.statusCode === 410 || err.statusCode === 404 || err.code === 'ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY' ) { - console.error('Subscription has expired or is no longer valid:', error); + log.error('Subscription has expired or is no longer valid:', error); try { const deleteNotificationSubscriptionByPk = await mutate( DeleteNotificationSubscriptionByPkDocument, @@ -41,15 +41,13 @@ export function pushWebNotification(payload: NotificationPayload) { }, 'deleteNotificationSubscriptionByPk', ); - console.log( - 'Deleted subscription', - deleteNotificationSubscriptionByPk, - ); + log.info('Deleted subscription', deleteNotificationSubscriptionByPk); } catch (error) { - console.error('Error deleting subscription', error); + log.error('Error deleting subscription', error); } + return; } - + log.error('webpush error', JSON.stringify(error, null, 2)); throw error; } }; diff --git a/apps/main/src/server/services/notification/send-notification-via-email.ts b/apps/main/src/server/services/notification/send-notification-via-email.ts index 7730ae28c..1f963fadf 100644 --- a/apps/main/src/server/services/notification/send-notification-via-email.ts +++ b/apps/main/src/server/services/notification/send-notification-via-email.ts @@ -1,3 +1,5 @@ +import { log } from 'next-axiom'; + import { NotificationType_Enum } from '$/graphql/generated/types'; import { sendNotificationEmail } from '../email/send-emails'; @@ -5,7 +7,7 @@ import { NotificationPayload } from './types'; export async function sendNotificationViaEmail(payload: NotificationPayload) { if (!payload.recipient.email) { - console.warn(`No recipient email`, payload); + log.warn(`No recipient email`, payload); return; } await sendNotificationEmail({ diff --git a/apps/main/src/server/services/notification/send.ts b/apps/main/src/server/services/notification/send.ts index e80f79372..cad0ec391 100644 --- a/apps/main/src/server/services/notification/send.ts +++ b/apps/main/src/server/services/notification/send.ts @@ -1,3 +1,5 @@ +import { log } from 'next-axiom'; + import { query } from '$/server/common/gql'; import { NotificationSubscriptionsByUserIdDocument } from '$/server/graphql/generated/notification'; @@ -18,7 +20,7 @@ export async function sendNotification(payload: NotificationPayload) { !Array.isArray(notificationSubscriptions) || notificationSubscriptions.length === 0 ) { - console.error('No subscriptions found'); + log.error('No subscriptions found'); return; } await Promise.allSettled([ diff --git a/apps/main/src/server/services/user.ts b/apps/main/src/server/services/user.ts index cab7e4a93..c0feae5b4 100644 --- a/apps/main/src/server/services/user.ts +++ b/apps/main/src/server/services/user.ts @@ -1,4 +1,5 @@ import { Profile as AuthProfile } from 'next-auth'; +import { log } from 'next-axiom'; import { UpdateUserByPkDocument, @@ -43,7 +44,7 @@ export async function fillUserFields( return; } catch (error) { - console.error('fill user fields failed', error); + log.error('fill user fields failed', error); return; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0e1c3226..96b88cd25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,6 +223,7 @@ importers: msw-storybook-addon: 1.6.3 next: 12.2.5 next-auth: 4.10.3 + next-axiom: ^0.13.0 next-compose-plugins: 2.2.1 next-connect: 0.12.2 next-mdx-remote: 3.0.8 @@ -305,6 +306,7 @@ importers: lodash: 4.17.21 next: 12.2.5_zazhvxb5wdpfvem7m5mnvhb45a next-auth: 4.10.3_znuz5irtsx3b6gfuzekl2h7mzq_mnimnj2pdeccz5uivmdmoly3tq + next-axiom: 0.13.0_next@12.2.5 next-compose-plugins: 2.2.1 next-connect: 0.12.2 next-mdx-remote: 3.0.8_biqbaboplfbrettd7655fr4n2y @@ -16777,6 +16779,16 @@ packages: dev: false patched: true + /next-axiom/0.13.0_next@12.2.5: + resolution: {integrity: sha512-yWNsTd2abreXa9KBYeAhhVCsTFClZI37jUG0c+CfkENxl51EpSVlY+dWSA8hGJQtXWOXKs6yeW1PaiVr2NU1rw==} + engines: {node: '>=15'} + peerDependencies: + next: ^12.1.4 + dependencies: + next: 12.2.5_zazhvxb5wdpfvem7m5mnvhb45a + whatwg-fetch: 3.6.2 + dev: false + /next-compose-plugins/2.2.1: resolution: {integrity: sha512-OjJ+fV15FXO2uQXQagLD4C0abYErBjyjE0I0FHpOEIB8upw0hg1ldFP6cqHTJBH1cZqy96OeR3u1dJ+Ez2D4Bg==} dev: false @@ -23186,7 +23198,6 @@ packages: /whatwg-fetch/3.6.2: resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} - dev: true /whatwg-mimetype/3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} From 2fcf0e8f7e0c57b0ecefa11ef94b2b59e6495fa4 Mon Sep 17 00:00:00 2001 From: Qing Date: Fri, 26 Aug 2022 18:12:47 +0800 Subject: [PATCH 2/3] refactor: use axiom log instead of console --- apps/main/package.json | 2 +- apps/main/src/__tests__/pages/auth/welcome.test.tsx | 3 ++- apps/main/src/blocks/analytics/stats/modals/sources.tsx | 2 +- apps/main/src/blocks/comment-card/comment-card.tsx | 3 ++- .../stories/notification-hub.stories.tsx | 2 +- apps/main/src/blocks/project-card/page-view-stats.tsx | 3 ++- apps/main/src/blocks/project-card/project-card.tsx | 3 ++- .../src/contexts/comment-context/use-create-a-comment.ts | 5 +++-- .../contexts/comment-context/use-toggle-a-like-action.ts | 7 ++++--- .../notification-context/notification-provider.tsx | 5 +++-- .../notification-context/use-reload-when-sw-change.ts | 3 ++- apps/main/src/contexts/notification-context/utilities.ts | 7 ++++--- apps/main/src/hooks/use-local-storage.ts | 9 +++++---- apps/main/src/lib/logger.ts | 3 +++ apps/main/src/pages/_app.tsx | 2 +- apps/main/src/pages/api/auth/[...nextauth].ts | 4 ++-- apps/main/src/pages/api/auth/delete/facebook.ts | 4 ++-- apps/main/src/pages/blog/[...slug].tsx | 2 +- apps/main/src/pages/profile/index.tsx | 3 ++- apps/main/src/pages/widget/comment/[pageURL].tsx | 7 ++++--- .../src/pages/widget/comment/timeline/[commentId].tsx | 7 ++++--- apps/main/src/server/services/email/send.ts | 2 +- .../src/server/services/mutation-event/like-handler.ts | 2 +- apps/main/src/server/utilities/create-token.ts | 2 +- packages/eslint-config-chirpy/index.js | 2 ++ pnpm-lock.yaml | 2 +- 26 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 apps/main/src/lib/logger.ts diff --git a/apps/main/package.json b/apps/main/package.json index 48cde2f24..9eae640d1 100644 --- a/apps/main/package.json +++ b/apps/main/package.json @@ -58,7 +58,7 @@ "lodash": "4.17.21", "next": "12.2.5", "next-auth": "4.10.3", - "next-axiom": "^0.13.0", + "next-axiom": "0.13.0", "next-compose-plugins": "2.2.1", "next-connect": "0.12.2", "next-mdx-remote": "3.0.8", diff --git a/apps/main/src/__tests__/pages/auth/welcome.test.tsx b/apps/main/src/__tests__/pages/auth/welcome.test.tsx index 7eda21545..251004335 100644 --- a/apps/main/src/__tests__/pages/auth/welcome.test.tsx +++ b/apps/main/src/__tests__/pages/auth/welcome.test.tsx @@ -5,10 +5,11 @@ import { pageRender } from '$/__tests__/fixtures/page-render'; import { setMockedUser } from '$/__tests__/fixtures/page-render'; import { mockNextRouter } from '$/__tests__/mocks/next-router'; import * as UserModule from '$/graphql/generated/user'; +import { logger } from '$/lib/logger'; import Welcome from '$/pages/auth/welcome'; const mockUpdateUser = jest.fn().mockImplementation(() => { - console.log('called mockUpdateUser'); + logger.debug('called mockUpdateUser'); }); jest.mock('$/graphql/generated/user', () => ({ // Make exported object configurable diff --git a/apps/main/src/blocks/analytics/stats/modals/sources.tsx b/apps/main/src/blocks/analytics/stats/modals/sources.tsx index 99bf6f8b1..cc64db974 100644 --- a/apps/main/src/blocks/analytics/stats/modals/sources.tsx +++ b/apps/main/src/blocks/analytics/stats/modals/sources.tsx @@ -109,7 +109,7 @@ class SourcesModal extends React.Component { if (filter === 'utm_sources') query.set('utm_source', source.name); if (filter === 'utm_campaigns') query.set('utm_campaign', source.name); - // console.log(source); + // logger.debug(source); return ( diff --git a/apps/main/src/blocks/comment-card/comment-card.tsx b/apps/main/src/blocks/comment-card/comment-card.tsx index f2f8619d4..fc1998fab 100644 --- a/apps/main/src/blocks/comment-card/comment-card.tsx +++ b/apps/main/src/blocks/comment-card/comment-card.tsx @@ -17,6 +17,7 @@ import { useToast } from '$/components/toast'; import { useCommentContext } from '$/contexts/comment-context'; import { useCurrentUser } from '$/contexts/current-user-context'; import { COMMENT_TREE_MAX_DEPTH } from '$/lib/constants'; +import { logger } from '$/lib/logger'; import { isENVDev } from '$/server/utilities/env'; import { CommentLeafType } from '$/types/widget'; import { dayjs } from '$/utilities/date'; @@ -71,7 +72,7 @@ export function CommentCard({ type: 'error', title: 'Replied failed, try again later', }); - console.error('Replied failed', error); + logger.error('Replied failed', error); } }; const handleDimissRTE = () => { diff --git a/apps/main/src/blocks/notification-hub/stories/notification-hub.stories.tsx b/apps/main/src/blocks/notification-hub/stories/notification-hub.stories.tsx index d2afee7e7..88ca6af99 100644 --- a/apps/main/src/blocks/notification-hub/stories/notification-hub.stories.tsx +++ b/apps/main/src/blocks/notification-hub/stories/notification-hub.stories.tsx @@ -39,7 +39,7 @@ Default.play = async ({ canvasElement }) => { function getUrqlParameter(data: CurrentNotificationMessagesSubscription) { return (op: Operation) => { const query = getOperationName(op.query); - // console.log({ op, subscription: query }); + // logger.debug({ op, subscription: query }); if (query === 'currentNotificationMessages') { return { data, diff --git a/apps/main/src/blocks/project-card/page-view-stats.tsx b/apps/main/src/blocks/project-card/page-view-stats.tsx index a8308761b..16353b259 100644 --- a/apps/main/src/blocks/project-card/page-view-stats.tsx +++ b/apps/main/src/blocks/project-card/page-view-stats.tsx @@ -5,6 +5,7 @@ import { IconArrowUp } from '$/components/icons'; import { Link } from '$/components/link'; import { Text } from '$/components/text'; import { ANALYTICS_DOMAIN } from '$/lib/constants'; +import { logger } from '$/lib/logger'; import { getStats } from '../analytics/analytics-api'; @@ -31,7 +32,7 @@ export function PageViewStats({ domain }: PageViewStatsProps): JSX.Element { } }) .catch(() => { - console.log('No stats data'); + logger.debug('No stats data'); }); }, [domain]); return ( diff --git a/apps/main/src/blocks/project-card/project-card.tsx b/apps/main/src/blocks/project-card/project-card.tsx index 2993a333e..138f31730 100644 --- a/apps/main/src/blocks/project-card/project-card.tsx +++ b/apps/main/src/blocks/project-card/project-card.tsx @@ -13,6 +13,7 @@ import { Text } from '$/components/text'; import { useToast } from '$/components/toast'; import { useDeleteProjectByPkMutation } from '$/graphql/generated/project'; import { UserDashboardProjectsQuery } from '$/graphql/generated/user'; +import { logger } from '$/lib/logger'; import { listHoverable } from '$/styles/common'; import { dayjs } from '$/utilities/date'; @@ -52,7 +53,7 @@ export function ProjectCard({ setDeletingProject(''); onDeletedProject(); } catch (error) { - console.error(error); + logger.error('Delete project failed', error); showToast({ type: 'error', title: diff --git a/apps/main/src/contexts/comment-context/use-create-a-comment.ts b/apps/main/src/contexts/comment-context/use-create-a-comment.ts index 5cbb18f9f..f3246ca47 100644 --- a/apps/main/src/contexts/comment-context/use-create-a-comment.ts +++ b/apps/main/src/contexts/comment-context/use-create-a-comment.ts @@ -3,6 +3,7 @@ import { JsonArray } from 'type-fest'; import { RTEValue } from '$/blocks/rich-text-editor'; import { useToast } from '$/components/toast'; import { useInsertOneCommentMutation } from '$/graphql/generated/comment'; +import { logger } from '$/lib/logger'; import { useCurrentUser } from '../current-user-context'; @@ -19,7 +20,7 @@ export function useCreateAComment({ pageId }: useCreateACommentOptions) { const createAComment = async (reply: RTEValue, commentId?: string) => { if (!isSignIn) { - console.error('Navigate to sign-in page'); + logger.error('Navigate to sign-in page'); throw undefined; } const { data } = await insertOneComment({ @@ -32,7 +33,7 @@ export function useCreateAComment({ pageId }: useCreateACommentOptions) { type: 'error', title: `Server didn't respond, please try again later.`, }); - console.error(`Can't insert a comment, parentId ${commentId}`); + logger.error(`Can't insert a comment, parentId ${commentId}`); } }; return createAComment; diff --git a/apps/main/src/contexts/comment-context/use-toggle-a-like-action.ts b/apps/main/src/contexts/comment-context/use-toggle-a-like-action.ts index 2e312560d..d7b93c255 100644 --- a/apps/main/src/contexts/comment-context/use-toggle-a-like-action.ts +++ b/apps/main/src/contexts/comment-context/use-toggle-a-like-action.ts @@ -5,6 +5,7 @@ import { useInsertOneLikeMutation, } from '$/graphql/generated/like'; import { useSignInWindow } from '$/hooks/use-sign-in-window'; +import { logger } from '$/lib/logger'; export type UseToggleALikeAction = ReturnType; @@ -32,7 +33,7 @@ export function useToggleALikeAction() { id: likeId, }); if (!data?.deleteLikeByPk?.id) { - console.error(`Can't delete the like, id ${likeId}`); + logger.error(`Can't delete the like, id ${likeId}`); } } else { try { @@ -44,7 +45,7 @@ export function useToggleALikeAction() { type: 'error', title: `Server didn't respond, please try again later.`, }); - console.error(`Can't create a like action`); + logger.error(`Can't create a like action`); } } catch (error) { showToast({ @@ -54,7 +55,7 @@ export function useToggleALikeAction() { // There is a `Unique constraint failed on the fields: (`userId`,`commentId`)` error // when a user click the like button again during this API processing // TODO: Refresh UI immediately, call APIs in the background - console.error(error); + logger.error('Insert a like', error); } } }; diff --git a/apps/main/src/contexts/notification-context/notification-provider.tsx b/apps/main/src/contexts/notification-context/notification-provider.tsx index a330e193b..2a8147742 100644 --- a/apps/main/src/contexts/notification-context/notification-provider.tsx +++ b/apps/main/src/contexts/notification-context/notification-provider.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; +import { logger } from '$/lib/logger'; import { asyncNoop, noop } from '$/utilities/isomorphic/function'; import { useCurrentUser } from '../current-user-context'; @@ -37,14 +38,14 @@ export function NotificationProvider({ children }: INotificationProviderProps) { return; } if (Notification.permission === 'denied') { - return console.log('Notification permission denied'); + return logger.debug('Notification permission denied'); } else if (Notification.permission === 'default') { const permission = await askNotificationPermission(); if (permission === 'denied') { setDidDeny(true); } if (permission === 'denied' || permission === 'default') { - return console.log(`User didn't grant notification permission`); + return logger.debug(`User didn't grant notification permission`); } } diff --git a/apps/main/src/contexts/notification-context/use-reload-when-sw-change.ts b/apps/main/src/contexts/notification-context/use-reload-when-sw-change.ts index 82b4184e7..359caca88 100644 --- a/apps/main/src/contexts/notification-context/use-reload-when-sw-change.ts +++ b/apps/main/src/contexts/notification-context/use-reload-when-sw-change.ts @@ -1,6 +1,7 @@ import * as React from 'react'; import { useToast } from '$/components/toast'; +import { logger } from '$/lib/logger'; import { checkServiceWorkerCompatibility } from './utilities'; @@ -18,7 +19,7 @@ export function useReloadWhenSwChange() { navigator.serviceWorker.addEventListener('controllerchange', () => { if (refreshing) return; // prevent infinite refresh loop when we use "Update on Reload" in DevTool refreshing = true; - console.log('Controller loaded'); + logger.debug('Controller loaded'); window.location.reload(); }); }, []); diff --git a/apps/main/src/contexts/notification-context/utilities.ts b/apps/main/src/contexts/notification-context/utilities.ts index 76ee1f6de..303a69068 100644 --- a/apps/main/src/contexts/notification-context/utilities.ts +++ b/apps/main/src/contexts/notification-context/utilities.ts @@ -1,3 +1,4 @@ +import { logger } from '$/lib/logger'; import { urlBase64ToUint8Array } from '$/utilities/string'; const NOTIFICATION_DID_REGISTER_KEY = @@ -42,7 +43,7 @@ export function registerNotificationSubscription(): Promise { }); }) .catch((error) => { - console.error('Service worker registration failed', error); + logger.error('Service worker registration failed', error); throw error; }); } @@ -51,7 +52,7 @@ const SERVICE_WORKER_ERROR = 'Service worker not supported'; export function checkServiceWorkerCompatibility(): boolean { const supported = 'serviceWorker' in navigator; if (!supported) { - console.error(SERVICE_WORKER_ERROR); + logger.error(SERVICE_WORKER_ERROR); } return supported; } @@ -60,7 +61,7 @@ const NOTIFICATION_ERROR = 'This browser does not support notifications.'; export function checkNotificationCompatibility(): boolean { const supported = 'Notification' in window; if (!supported) { - console.error(NOTIFICATION_ERROR); + logger.error(NOTIFICATION_ERROR); } return supported; } diff --git a/apps/main/src/hooks/use-local-storage.ts b/apps/main/src/hooks/use-local-storage.ts index 63863172e..ec95205c4 100644 --- a/apps/main/src/hooks/use-local-storage.ts +++ b/apps/main/src/hooks/use-local-storage.ts @@ -1,5 +1,6 @@ import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { logger } from '$/lib/logger'; import { isSSRMode } from '$/utilities/env'; import { useEventListener } from './use-event-listener'; @@ -21,7 +22,7 @@ export function useLocalStorage( const item = window.localStorage.getItem(key); return item ? (parseJSON(item) as T) : initialValue; } catch (error) { - console.warn(`Error reading localStorage key “${key}”:`, error); + logger.warn(`Error reading localStorage key “${key}”:`, error); return initialValue; } }; @@ -30,7 +31,7 @@ export function useLocalStorage( const customEventKey = `local-storage.${key}` as const; const setValue: SetValue = (value) => { if (typeof window == 'undefined') { - console.warn( + logger.warn( `Tried setting localStorage key “${key}” even though environment is not a client`, ); } @@ -47,7 +48,7 @@ export function useLocalStorage( window.dispatchEvent(new Event(customEventKey)); } catch (error) { - console.warn(`Error setting localStorage key “${key}”:`, error); + logger.warn(`Error setting localStorage key “${key}”:`, error); } }; @@ -79,7 +80,7 @@ function parseJSON(value: string | null): T | undefined { try { return value === 'undefined' ? undefined : JSON.parse(value ?? ''); } catch { - console.log('parsing error on', { value }); + logger.warn('parsing error on', { value }); return undefined; } } diff --git a/apps/main/src/lib/logger.ts b/apps/main/src/lib/logger.ts new file mode 100644 index 000000000..23eabe778 --- /dev/null +++ b/apps/main/src/lib/logger.ts @@ -0,0 +1,3 @@ +import { log } from 'next-axiom/dist/logger'; + +export const logger = log.with({ scope: 'frontend' }); diff --git a/apps/main/src/pages/_app.tsx b/apps/main/src/pages/_app.tsx index 8652573da..59b3d4659 100644 --- a/apps/main/src/pages/_app.tsx +++ b/apps/main/src/pages/_app.tsx @@ -50,7 +50,7 @@ function App({ export default App; -export { reportWebVitals } from 'next-axiom'; +export { reportWebVitals } from 'next-axiom/dist/webVitals'; export const loadFeatures = () => import( diff --git a/apps/main/src/pages/api/auth/[...nextauth].ts b/apps/main/src/pages/api/auth/[...nextauth].ts index 5843bb86f..400b238d1 100644 --- a/apps/main/src/pages/api/auth/[...nextauth].ts +++ b/apps/main/src/pages/api/auth/[...nextauth].ts @@ -1,5 +1,5 @@ import NextAuth from 'next-auth'; -import { withAxiom } from 'next-axiom'; +import { log, withAxiom } from 'next-axiom'; import { HASURA_TOKEN_MAX_AGE, SESSION_MAX_AGE } from '$/lib/constants'; import { query } from '$/server/common/gql'; @@ -89,7 +89,7 @@ export default withAxiom( events: { async createUser({ user }) { if (!user.email) { - return console.info('Create an anonymous user'); + return log.info('Create an anonymous user'); } await sendWelcomeLetter({ to: { diff --git a/apps/main/src/pages/api/auth/delete/facebook.ts b/apps/main/src/pages/api/auth/delete/facebook.ts index 29ad4c184..dd66f8ce4 100644 --- a/apps/main/src/pages/api/auth/delete/facebook.ts +++ b/apps/main/src/pages/api/auth/delete/facebook.ts @@ -1,6 +1,6 @@ import crypto from 'crypto'; import { NextApiRequest, NextApiResponse } from 'next'; -import { withAxiom } from 'next-axiom'; +import { log, withAxiom } from 'next-axiom'; import { getApiHandler } from '$/server/common/api-handler'; import { ApiError } from '$/server/common/error'; @@ -10,7 +10,7 @@ import { DeleteUserDocument } from '$/server/graphql/generated/user'; const handler = getApiHandler(); handler.post(async (req: NextApiRequest, res: NextApiResponse) => { const signedRequest = req.body.signed_request; - console.log('Validating signature', { + log.debug('Validating signature', { signedRequest, }); const [signature, payload] = diff --git a/apps/main/src/pages/blog/[...slug].tsx b/apps/main/src/pages/blog/[...slug].tsx index cf9ead17c..1c6b94d7e 100644 --- a/apps/main/src/pages/blog/[...slug].tsx +++ b/apps/main/src/pages/blog/[...slug].tsx @@ -67,7 +67,7 @@ export const getStaticProps: GetStaticProps< if (!params?.slug) { return { notFound: true }; } - // console.log({ slug: params.slug }); + // log.debug({ slug: params.slug }); const [mdxProps, directories] = await Promise.all([ getMDXPropsBySlug([CONTAINER_FOLDER, ...params.slug].join('/')), getDirectories(CONTAINER_FOLDER, `/${CONTAINER_FOLDER}`), diff --git a/apps/main/src/pages/profile/index.tsx b/apps/main/src/pages/profile/index.tsx index 6026fcf9a..382dde501 100644 --- a/apps/main/src/pages/profile/index.tsx +++ b/apps/main/src/pages/profile/index.tsx @@ -22,6 +22,7 @@ import { useToast } from '$/components/toast'; import { useCurrentUser } from '$/contexts/current-user-context'; import { useUpdateUserByPkMutation } from '$/graphql/generated/user'; import { useForm } from '$/hooks/use-form'; +import { logger } from '$/lib/logger'; import { EMAIL_REGEXP } from '$/utilities/validator'; type FormFields = { @@ -75,7 +76,7 @@ export default function Profile(): JSX.Element { twitterUserName: fields.twitter, }); if (error) { - console.error(error); + logger.error('Update user profile failed', error); if (error.message.includes(`unique constraint \"User_email_key\"`)) { showToast({ type: 'error', diff --git a/apps/main/src/pages/widget/comment/[pageURL].tsx b/apps/main/src/pages/widget/comment/[pageURL].tsx index 2b77acec4..473016fa0 100644 --- a/apps/main/src/pages/widget/comment/[pageURL].tsx +++ b/apps/main/src/pages/widget/comment/[pageURL].tsx @@ -5,6 +5,7 @@ import { GetStaticPropsContext, GetStaticPaths, } from 'next'; +import { log } from 'next-axiom'; import * as React from 'react'; import superjson from 'superjson'; import { OperationResult } from 'urql'; @@ -142,7 +143,7 @@ export const getStaticProps: GetStaticProps< >( client.subscription(CommentTreeDocument, { pageURL }), subscribe((result) => { - // console.log(result); + // log.debug(result); resolve(result); }), ); @@ -160,7 +161,7 @@ export const getStaticProps: GetStaticProps< 'pageByPk', ); if (!pageByPk) { - console.error(`Can't find theme info`); + log.error(`Can't find theme info`); return { notFound: true }; } return { @@ -175,7 +176,7 @@ export const getStaticProps: GetStaticProps< revalidate: 5 * 60, }; } catch (error) { - console.error(superjson.stringify(error)); + log.error(superjson.stringify(error)); return { notFound: true }; } }; diff --git a/apps/main/src/pages/widget/comment/timeline/[commentId].tsx b/apps/main/src/pages/widget/comment/timeline/[commentId].tsx index a25542611..30c8a202f 100644 --- a/apps/main/src/pages/widget/comment/timeline/[commentId].tsx +++ b/apps/main/src/pages/widget/comment/timeline/[commentId].tsx @@ -6,6 +6,7 @@ import { InferGetStaticPropsType, GetStaticPropsResult, } from 'next'; +import { log } from 'next-axiom'; import * as React from 'react'; import superjson from 'superjson'; import { OperationResult } from 'urql'; @@ -122,7 +123,7 @@ export const getStaticProps: GetStaticProps = async ({ pipe, any>( client.subscription(CommentTimelineDocument, { id: commentId }), subscribe((result) => { - // console.log(result); + // log.debug(result); resolve(result); }), ); @@ -140,7 +141,7 @@ export const getStaticProps: GetStaticProps = async ({ 'pageByPk', ); if (!pageByPk) { - console.error(`Can't find page info`); + log.error(`Can't find page info`); return { notFound: true }; } @@ -156,7 +157,7 @@ export const getStaticProps: GetStaticProps = async ({ revalidate: 600, }; } catch (error) { - console.error(superjson.stringify(error)); + log.error(superjson.stringify(error)); return { notFound: true }; } }; diff --git a/apps/main/src/server/services/email/send.ts b/apps/main/src/server/services/email/send.ts index 4db7be314..4b6109892 100644 --- a/apps/main/src/server/services/email/send.ts +++ b/apps/main/src/server/services/email/send.ts @@ -32,7 +32,7 @@ export async function sendEmail({ subject, to, type, content }: EmailOptions) { email.sender = senders[type]; email.htmlContent = content; email.tags = [type]; - // console.log('Send email', JSON.stringify(email, null, 2)); + // log.debug('Send email', JSON.stringify(email, null, 2)); await sibApi.sendTransacEmail(email); } diff --git a/apps/main/src/server/services/mutation-event/like-handler.ts b/apps/main/src/server/services/mutation-event/like-handler.ts index fd7bcb755..ba5ba1cb7 100644 --- a/apps/main/src/server/services/mutation-event/like-handler.ts +++ b/apps/main/src/server/services/mutation-event/like-handler.ts @@ -21,7 +21,7 @@ export async function handleLikeEvent( eventBody: EventPayload, res: NextApiResponse, ): Promise { - // console.log(JSON.stringify(eventBody, null, 2)); + // log.debug(JSON.stringify(eventBody, null, 2)); if (!isEventLike(eventBody)) { return; } diff --git a/apps/main/src/server/utilities/create-token.ts b/apps/main/src/server/utilities/create-token.ts index e6750f8b7..8486dad1c 100644 --- a/apps/main/src/server/utilities/create-token.ts +++ b/apps/main/src/server/utilities/create-token.ts @@ -38,7 +38,7 @@ function createToken( algorithm: process.env.HASH_ALGORITHM as jwt.Algorithm, expiresIn: maxAge, }); - // console.log({ payload, key: process.env.NEXTAUTH_SECRET }); + // log.debug({ payload, key: process.env.NEXTAUTH_SECRET }); return encodedToken; } diff --git a/packages/eslint-config-chirpy/index.js b/packages/eslint-config-chirpy/index.js index bce805610..1edc04504 100644 --- a/packages/eslint-config-chirpy/index.js +++ b/packages/eslint-config-chirpy/index.js @@ -17,6 +17,8 @@ module.exports = { }, }, rules: { + 'no-console': 'error', + '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-namespace': 'off', '@typescript-eslint/no-explicit-any': 'off', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96b88cd25..de89ff36c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,7 +223,7 @@ importers: msw-storybook-addon: 1.6.3 next: 12.2.5 next-auth: 4.10.3 - next-axiom: ^0.13.0 + next-axiom: 0.13.0 next-compose-plugins: 2.2.1 next-connect: 0.12.2 next-mdx-remote: 3.0.8 From a56350e0ffa47224b01b17d727276ce1b9179c45 Mon Sep 17 00:00:00 2001 From: Qing Date: Sat, 27 Aug 2022 10:56:36 +0800 Subject: [PATCH 3/3] fix: catch notification subscription error --- .vscode/launch.json | 7 +++++++ .../src/contexts/notification-context/utilities.ts | 12 ++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 563bde043..15c993fbf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,13 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "cwd": "${workspaceFolder}/apps/main" + }, { "type": "node", "request": "attach", diff --git a/apps/main/src/contexts/notification-context/utilities.ts b/apps/main/src/contexts/notification-context/utilities.ts index 303a69068..839a07c76 100644 --- a/apps/main/src/contexts/notification-context/utilities.ts +++ b/apps/main/src/contexts/notification-context/utilities.ts @@ -36,10 +36,14 @@ export function registerNotificationSubscription(): Promise { 'Content-type': 'application/json', }, body: JSON.stringify({ subscription }), - }).then((rsp) => { - sessionStorage.setItem(NOTIFICATION_DID_REGISTER_KEY, 'true'); - return rsp; - }); + }) + .then((rsp) => { + sessionStorage.setItem(NOTIFICATION_DID_REGISTER_KEY, 'true'); + return rsp; + }) + .catch((error) => { + logger.warn('Register notification subscription', error); + }); }); }) .catch((error) => {