From e914c8efccafb1a5eb8568cb6858a1e966f78e40 Mon Sep 17 00:00:00 2001 From: Richard Guerre Date: Mon, 7 Jun 2021 18:19:57 +0200 Subject: [PATCH 1/6] Log table + migration --- electron/app/models/Log.ts | 20 +++++++++++++++++++ .../20210607180611_create-Logs-table.js | 14 +++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 electron/app/models/Log.ts create mode 100644 electron/migrations/20210607180611_create-Logs-table.js diff --git a/electron/app/models/Log.ts b/electron/app/models/Log.ts new file mode 100644 index 00000000..de82a070 --- /dev/null +++ b/electron/app/models/Log.ts @@ -0,0 +1,20 @@ +import { Model } from 'objection'; + +/** + * This Log model will primarily be used to keep track of all errors in the DevTime app. + * This table can be extended to also keep track of other kinds of logs. Things that don't need to be saved to GitStart's database. + */ +export class Log extends Model { + static tableName = 'Logs'; + + id!: number; + createdAt!: Date; + updatedAt!: Date; + type!: 'ERROR' | string; + message?: string; + jsonData?: any; + + static get jsonAttributes() { + return ['jsonData']; + } +} diff --git a/electron/migrations/20210607180611_create-Logs-table.js b/electron/migrations/20210607180611_create-Logs-table.js new file mode 100644 index 00000000..edda7cb1 --- /dev/null +++ b/electron/migrations/20210607180611_create-Logs-table.js @@ -0,0 +1,14 @@ +exports.up = function (knex) { + return knex.schema.createTable('Logs', (table) => { + table.increments('id').notNullable(); + table.dateTime('createdAt').notNullable(); + table.dateTime('updatedAt').notNullable(); + table.string('type').notNullable(); + table.string('message'); + table.string('jsonData'); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTable('Logs'); +}; From c17645b7004c74ea0bddd7be02cb444f35317b71 Mon Sep 17 00:00:00 2001 From: Richard Guerre Date: Mon, 7 Jun 2021 18:52:05 +0200 Subject: [PATCH 2/6] create LogService --- electron/app/services/log-service.ts | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 electron/app/services/log-service.ts diff --git a/electron/app/services/log-service.ts b/electron/app/services/log-service.ts new file mode 100644 index 00000000..0db9f2fe --- /dev/null +++ b/electron/app/services/log-service.ts @@ -0,0 +1,36 @@ +import { logManager } from '../log-manager'; +import { Log } from '../models/Log'; + +export class LogService { + logger = logManager.getLogger('LogService'); + lastLog: null | Log = null; + + async createUpdateLog(logAttributes: Partial): Promise { + let log: Log = null; + if ( + logAttributes.type === this.lastLog.type && + logAttributes.message === this.lastLog.message && + logAttributes.jsonData === this.lastLog.jsonData + ) { + const now = new Date(); + await Log.query().findById(this.lastLog.id).patch({ + updatedAt: now, + }); + log = this.lastLog; + log.updatedAt = now; + } else { + log = await Log.query().insert(logAttributes); + } + this.lastLog = log; + return log; + } + + async findAllLogs(from: Date, to?: Date) { + return Log.query() + .where('updatedAt', '>=', from) + .where('updatedAt', '<=', to) + .orderBy('updatedAt', 'DESC'); + } +} + +export const logService = new LogService(); From e5bce8ceb5536ac2e4268e52dad1c7a999c1ca54 Mon Sep 17 00:00:00 2001 From: Richard Guerre Date: Mon, 7 Jun 2021 22:22:04 +0200 Subject: [PATCH 3/6] create findAllLogs API endpoint --- client/src/services/logs-api.ts | 9 +++++++++ electron/app/API.ts | 11 ++++++++++- electron/app/services/log-service.ts | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 client/src/services/logs-api.ts diff --git a/client/src/services/logs-api.ts b/client/src/services/logs-api.ts new file mode 100644 index 00000000..c4f12396 --- /dev/null +++ b/client/src/services/logs-api.ts @@ -0,0 +1,9 @@ +import { emit } from 'eiphop'; + +export async function findAllLogs(from: Date, to: Date) { + const data = await emit('findAllLogs', { + from, + to, + }); + return data; +} diff --git a/electron/app/API.ts b/electron/app/API.ts index 40ef7486..d1dc07bb 100644 --- a/electron/app/API.ts +++ b/electron/app/API.ts @@ -7,6 +7,7 @@ import { trackItemService } from './services/track-item-service'; import { stateManager } from './state-manager'; import { State } from './enums/state'; import { appConstants } from './app-constants'; +import { logService } from './services/log-service'; const settingsActions = { fetchAnalyserSettingsJsonString: async (req, res) => { @@ -42,6 +43,14 @@ const appSettingsActions = { res.send(data); }, }; + +const logsActions = { + findAllLogs: async (req, res) => { + const data = await logService.findAllLogs(req.from, req.to); + res.send(data); + }, +}; + const trackItemActions = { findAllDayItems: async ({ payload }, res) => { const data = await trackItemService.findAllDayItems( @@ -96,6 +105,6 @@ const trackItemActions = { export const initIpcActions = () => setupMainHandler( { ipcMain } as any, - { ...settingsActions, ...appSettingsActions, ...trackItemActions }, + { ...settingsActions, ...appSettingsActions, ...logsActions, ...trackItemActions }, true, ); diff --git a/electron/app/services/log-service.ts b/electron/app/services/log-service.ts index 0db9f2fe..aa07d2e9 100644 --- a/electron/app/services/log-service.ts +++ b/electron/app/services/log-service.ts @@ -5,7 +5,7 @@ export class LogService { logger = logManager.getLogger('LogService'); lastLog: null | Log = null; - async createUpdateLog(logAttributes: Partial): Promise { + async createOrUpdateLog(logAttributes: Partial): Promise { let log: Log = null; if ( logAttributes.type === this.lastLog.type && From 04f7a509093025ca4df96d84e6877faf6755eab9 Mon Sep 17 00:00:00 2001 From: Richard Guerre Date: Mon, 7 Jun 2021 22:57:20 +0200 Subject: [PATCH 4/6] add LogLIst component in SettingsPage --- client/src/components/Settings/LogList.tsx | 60 +++++++++++++++++++ client/src/components/Settings/LoginForm.tsx | 22 ++++++- .../src/components/Settings/SettingsForm.tsx | 4 ++ client/src/services/logs-api.ts | 2 +- 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 client/src/components/Settings/LogList.tsx diff --git a/client/src/components/Settings/LogList.tsx b/client/src/components/Settings/LogList.tsx new file mode 100644 index 00000000..f139b8a5 --- /dev/null +++ b/client/src/components/Settings/LogList.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useState } from 'react'; +import { Alert } from 'antd'; +import { findAllLogs } from '../../services/logs-api'; +import moment from 'moment'; + +type Log = { + id: number; + createdAt: Date; + updatedAt: Date; + type: 'ERROR' | string; + message?: string; + jsonData?: string; +}; + +export const LogList = () => { + const [logs, setLogs] = useState([]); + + const getAllLogs = async () => { + const newLogs = await findAllLogs( + moment() + .subtract(1, 'day') + .toDate(), + ); + setLogs(newLogs); + }; + + const handleDismiss = () => { + // TODO: handle dismissing of logs + }; + + useEffect(() => { + if (logs.length === 0) { + getAllLogs(); + } + const toCancel = setTimeout(() => { + getAllLogs(); + }, 5000); + + return () => clearInterval(toCancel); + }, []); + + if (logs.length === 0) { + return null; + } + + return ( + <> + {logs.map(log => ( + + ))} + + ); +}; diff --git a/client/src/components/Settings/LoginForm.tsx b/client/src/components/Settings/LoginForm.tsx index 5782481c..b40fd942 100644 --- a/client/src/components/Settings/LoginForm.tsx +++ b/client/src/components/Settings/LoginForm.tsx @@ -1,14 +1,30 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Button, Card, Typography } from 'antd'; -import { loginInExternalBrowser } from '../../services/settings.api'; +import { fetchLoginSettings, loginInExternalBrowser } from '../../services/settings.api'; const { Text } = Typography; export const LoginForm = () => { + // FIXME: This is duplicate code from client/src/components/MainLayout/MainLayout.tsx. Replace with a store to avoid fetching the same thing twice. + const [isLoggedIn, setIsLoggedIn] = useState(false); // avoid first render where alert is shown + + const checkLoginSettings = async () => { + const settings = await fetchLoginSettings(); + if (!settings) return; + + if (settings.token) { + setIsLoggedIn(true); + } + }; + + useEffect(() => { + checkLoginSettings(); + }, [location, location.pathname]); + return ( You must alreay have a GitStart account to login through here. Create an account at{' '} diff --git a/client/src/components/Settings/SettingsForm.tsx b/client/src/components/Settings/SettingsForm.tsx index 5b754cdf..0c766eb4 100644 --- a/client/src/components/Settings/SettingsForm.tsx +++ b/client/src/components/Settings/SettingsForm.tsx @@ -6,6 +6,7 @@ import { AppForm } from './AppForm'; import { WorkForm } from './WorkForm'; import { LoginForm } from './LoginForm'; import { ThemeSelect } from './ThemeSelect'; +import { LogList } from './LogList'; export const SettingsForm = () => { return ( @@ -27,6 +28,9 @@ export const SettingsForm = () => { {/* */} + + + diff --git a/client/src/services/logs-api.ts b/client/src/services/logs-api.ts index c4f12396..c47dd52b 100644 --- a/client/src/services/logs-api.ts +++ b/client/src/services/logs-api.ts @@ -1,6 +1,6 @@ import { emit } from 'eiphop'; -export async function findAllLogs(from: Date, to: Date) { +export async function findAllLogs(from: Date, to?: Date) { const data = await emit('findAllLogs', { from, to, From 88c6554ceb7cf1ca5ef2d1ed372fc86b3159a45f Mon Sep 17 00:00:00 2001 From: Richard Guerre Date: Mon, 7 Jun 2021 23:28:35 +0200 Subject: [PATCH 5/6] show errors in Settings page --- client/src/components/Settings/LogList.tsx | 11 +++++++---- client/src/components/Settings/LoginForm.tsx | 2 +- electron/app/API.ts | 4 ++-- electron/app/index.ts | 2 ++ electron/app/jobs/save-to-db.ts | 8 ++++++++ electron/app/models/Log.ts | 2 +- electron/app/services/log-service.ts | 7 +++++++ 7 files changed, 28 insertions(+), 8 deletions(-) diff --git a/client/src/components/Settings/LogList.tsx b/client/src/components/Settings/LogList.tsx index f139b8a5..5c5d455f 100644 --- a/client/src/components/Settings/LogList.tsx +++ b/client/src/components/Settings/LogList.tsx @@ -7,7 +7,7 @@ type Log = { id: number; createdAt: Date; updatedAt: Date; - type: 'ERROR' | string; + type: 'ERROR'; message?: string; jsonData?: string; }; @@ -34,9 +34,10 @@ export const LogList = () => { } const toCancel = setTimeout(() => { getAllLogs(); - }, 5000); + }, 10000); return () => clearInterval(toCancel); + // eslint-disable-next-line }, []); if (logs.length === 0) { @@ -48,8 +49,10 @@ export const LogList = () => { {logs.map(log => ( { useEffect(() => { checkLoginSettings(); - }, [location, location.pathname]); + }, []); return ( diff --git a/electron/app/API.ts b/electron/app/API.ts index d1dc07bb..bdcbf37e 100644 --- a/electron/app/API.ts +++ b/electron/app/API.ts @@ -45,8 +45,8 @@ const appSettingsActions = { }; const logsActions = { - findAllLogs: async (req, res) => { - const data = await logService.findAllLogs(req.from, req.to); + findAllLogs: async ({ payload }, res) => { + const data = await logService.findAllLogs(payload.from, payload.to); res.send(data); }, }; diff --git a/electron/app/index.ts b/electron/app/index.ts index 5c9a9f9c..58676fe9 100644 --- a/electron/app/index.ts +++ b/electron/app/index.ts @@ -13,6 +13,8 @@ import AppUpdater from './app-updater'; import config from './config'; import { appConstants } from './app-constants'; import { settingsService } from './services/settings-service'; +import { logService } from './services/log-service'; +import moment = require('moment'); let logger = logManager.getLogger('AppIndex'); app.setAppUserModelId(process.execPath); diff --git a/electron/app/jobs/save-to-db.ts b/electron/app/jobs/save-to-db.ts index af06b13e..97dd472e 100644 --- a/electron/app/jobs/save-to-db.ts +++ b/electron/app/jobs/save-to-db.ts @@ -3,6 +3,7 @@ import { appConstants } from '../app-constants'; import { fetchGraphQLClient, getUserFromToken } from '../graphql'; import { logManager } from '../log-manager'; import { TrackItem } from '../models/TrackItem'; +import { logService } from '../services/log-service'; import { settingsService } from '../services/settings-service'; import { trackItemService } from '../services/track-item-service'; @@ -172,6 +173,13 @@ export class SaveToDbJob { console.log('Successfully linked', items.length, `user_event with its TrackItem`); } catch (e) { console.error(e); + logService + .createOrUpdateLog({ + type: 'ERROR', + message: e.message, + jsonData: JSON.stringify(e), + }) + .catch(console.error); } } } diff --git a/electron/app/models/Log.ts b/electron/app/models/Log.ts index de82a070..3fbe7c89 100644 --- a/electron/app/models/Log.ts +++ b/electron/app/models/Log.ts @@ -10,7 +10,7 @@ export class Log extends Model { id!: number; createdAt!: Date; updatedAt!: Date; - type!: 'ERROR' | string; + type!: 'ERROR'; message?: string; jsonData?: any; diff --git a/electron/app/services/log-service.ts b/electron/app/services/log-service.ts index aa07d2e9..fa5510a4 100644 --- a/electron/app/services/log-service.ts +++ b/electron/app/services/log-service.ts @@ -8,6 +8,7 @@ export class LogService { async createOrUpdateLog(logAttributes: Partial): Promise { let log: Log = null; if ( + this.lastLog && logAttributes.type === this.lastLog.type && logAttributes.message === this.lastLog.message && logAttributes.jsonData === this.lastLog.jsonData @@ -19,6 +20,8 @@ export class LogService { log = this.lastLog; log.updatedAt = now; } else { + logAttributes.createdAt = new Date(); + logAttributes.updatedAt = new Date(); log = await Log.query().insert(logAttributes); } this.lastLog = log; @@ -26,9 +29,13 @@ export class LogService { } async findAllLogs(from: Date, to?: Date) { + if (!to) { + to = new Date(); + } return Log.query() .where('updatedAt', '>=', from) .where('updatedAt', '<=', to) + .skipUndefined() .orderBy('updatedAt', 'DESC'); } } From ebd1d98d1e998003ff517620382532fa080e01cd Mon Sep 17 00:00:00 2001 From: Richard Guerre Date: Tue, 8 Jun 2021 13:58:11 +0200 Subject: [PATCH 6/6] clearTimeout instead of clearInterval --- client/src/components/Settings/LogList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Settings/LogList.tsx b/client/src/components/Settings/LogList.tsx index 5c5d455f..58796327 100644 --- a/client/src/components/Settings/LogList.tsx +++ b/client/src/components/Settings/LogList.tsx @@ -36,7 +36,7 @@ export const LogList = () => { getAllLogs(); }, 10000); - return () => clearInterval(toCancel); + return () => clearTimeout(toCancel); // eslint-disable-next-line }, []);