Skip to content

Commit

Permalink
Merge pull request #18 from paywteam/feature/notifier
Browse files Browse the repository at this point in the history
Complete implementation for fragment notifier
  • Loading branch information
ihooni authored Dec 2, 2019
2 parents 9823efe + 25ebb43 commit 77f0967
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ DB_DATABASE=database-name
DB_USERNAME=username
DB_PASSWORD=password

MAIL_SERVICE=Zoho
MAIL_HOST=smtp.zoho.com
MAIL_PORT=465
MAIL_USERNAME=username
MAIL_PASSWORD=password

SESSION_SECRET=session-secret
SESSION_NAME=session-name

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
"moment": "^2.24.0",
"mongoose": "^5.7.3",
"nanoid": "^2.1.3",
"node-schedule": "^1.3.2",
"nodemailer": "^6.3.1",
"p-limit": "^2.2.1",
"passport": "^0.4.0",
"passport-google-oauth20": "^2.0.0",
Expand All @@ -64,6 +66,8 @@
"@types/module-alias": "^2.0.0",
"@types/mongoose": "^5.5.18",
"@types/nanoid": "^2.1.0",
"@types/node-schedule": "^1.2.4",
"@types/nodemailer": "^6.2.2",
"@types/passport": "^1.0.1",
"@types/passport-google-oauth20": "^2.0.2",
"@types/puppeteer": "^2.0.0",
Expand Down
49 changes: 49 additions & 0 deletions src/app/modules/Mailer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import nodeMailer, { Transporter } from 'nodemailer'
import LogHelper from '@/modules/LogHelper'

export default class Mailer {
private static _instance: Mailer

/**
* Mail transporter
*/
private readonly transporter: Transporter

/**
* Get singleton instance
*
* @constructor
*/
public static get Instance(): Mailer {
if (this._instance === undefined) {
this._instance = new this()
}

return this._instance
}

/**
* Private constructor for singleton pattern
*/
// eslint-disable-next-line no-useless-constructor
private constructor() {
this.transporter = nodeMailer.createTransport({
service: process.env.MAIL_SERVICE,
host: process.env.MAIL_HOST,
port: parseInt(process.env.MAIL_PORT, 10),
secure: true,
auth: {
user: process.env.MAIL_USERNAME,
pass: process.env.MAIL_PASSWORD
}
})
}

public sendMail(mailOptions: object): void {
this.transporter.sendMail(mailOptions, err => {
if (err) {
LogHelper.Instance.log('error', 'Mailer error: ' + err.stack)
}
})
}
}
32 changes: 28 additions & 4 deletions src/app/modules/webglue-api/FragmentNotifier.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import BrowserHandler from '@/modules/BrowserHandler'
import { UserDoc } from '@@/migrate/schemas/user'
import Mailer from '@/modules/Mailer'

interface Selector {
name: string
Expand All @@ -8,9 +10,16 @@ interface Selector {
export default class FragmentNotifier {
private static readonly VIEWPORT = { width: 1280, height: 800 }

// public notify(): void {}
public async notify(
user: UserDoc,
url: string,
selector: Selector
): Promise<void> {
const captureImg = await this.capture(url, selector)
this.sendCaptureToUser(user, url, captureImg)
}

public async capture(url: string, selector: Selector): Promise<string> {
private async capture(url: string, selector: Selector): Promise<string> {
const page = await BrowserHandler.Instance.browser.newPage()
await page.setViewport(FragmentNotifier.VIEWPORT)
await page.goto(url, { waitUntil: 'networkidle2' })
Expand All @@ -20,7 +29,7 @@ export default class FragmentNotifier {
return null
}

const capture = element.screenshot({
const capture = await element.screenshot({
encoding: 'base64'
})

Expand All @@ -29,5 +38,20 @@ export default class FragmentNotifier {
return capture
}

// public sendCaptureToUser(): void {}
private sendCaptureToUser(user: UserDoc, url: string, capture: string): void {
Mailer.Instance.sendMail({
from: '"webglue notifier" <[email protected]>',
to: user.email,
subject: 'webglue fragment 변화 알림',
html: `<a href="${url}"><img src="cid:[email protected]" /></a>`,
attachments: [
{
filename: 'fragment.png',
content: capture,
encoding: 'base64',
cid: '[email protected]' // same cid value as in the html img src
}
]
})
}
}
2 changes: 1 addition & 1 deletion src/app/modules/webglue-api/FragmentWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import moment from 'moment'
import pLimit from 'p-limit'
import Snappy from '@/modules/webglue-api/Snappy'
import { JSDOM } from 'jsdom'
import EventEmitter from 'events'
import { EventEmitter } from 'events'
import { GlueBoardDoc } from '@@/migrate/schemas/glue-board'
import { UserDoc } from '@@/migrate/schemas/user'

Expand Down
23 changes: 23 additions & 0 deletions src/app/providers/EventServiceProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import FragmentWatcher from '@/modules/webglue-api/FragmentWatcher'
import FragmentNotifier from '@/modules/webglue-api/FragmentNotifier'
import { UserDoc } from '@@/migrate/schemas/user'

export default class EventServiceProvider {
public static boot(): void {
this.registerFragmentEvent()
}

private static registerFragmentEvent(): void {
FragmentWatcher.Instance.on(
FragmentWatcher.CHANGE_EVENT,
(
user: UserDoc,
url: string,
selector: { name: string; offset: number }
) => {
const notifier = new FragmentNotifier()
notifier.notify(user, url, selector).then()
}
)
}
}
15 changes: 15 additions & 0 deletions src/app/providers/ScheduleServiceProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import schedule from 'node-schedule'
import FragmentWatcher from '@/modules/webglue-api/FragmentWatcher'

export default class ScheduleServiceProvider {
public static boot(): void {
this.scheduleWatcher()
}

private static scheduleWatcher(): void {
// watch fragment every 5 minutes
schedule.scheduleJob('*/5 * * * *', () => {
FragmentWatcher.Instance.watch().then()
})
}
}
8 changes: 8 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ import 'module-alias/register'
import dotenv from 'dotenv'
import DBServiceProvider from '@/providers/DBServiceProvider'
import RouteServiceProvider from '@/providers/RouteServiceProvider'
import EventServiceProvider from '@/providers/EventServiceProvider'
import ScheduleServiceProvider from '@/providers/ScheduleServiceProvider'
import BrowserHandler from '@/modules/BrowserHandler'

async function bootApp(): Promise<void> {
await DBServiceProvider.boot()
EventServiceProvider.boot()

await BrowserHandler.Instance.turnOn()

ScheduleServiceProvider.boot()
const app = RouteServiceProvider.boot()
app.listen(process.env.APP_PORT)
}
Expand Down
64 changes: 62 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,25 @@
dependencies:
"@types/node" "*"

"@types/node-schedule@^1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@types/node-schedule/-/node-schedule-1.2.4.tgz#2dce38dd7d87e77cd293113ac72f795fb9f6dfe0"
integrity sha512-s8ie8rUwAtX0Si75SiKH14akE/Ofw/Hx4Exbulv4wOQJEDerI7zeOaDODr/a1UEaHN19VkmZCt9Q0rFEKrVE5g==
dependencies:
"@types/node" "*"

"@types/node@*":
version "12.12.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.12.tgz#529bc3e73dbb35dd9e90b0a1c83606a9d3264bdb"
integrity sha512-MGuvYJrPU0HUwqF7LqvIj50RZUX23Z+m583KBygKYUZLlZ88n6w28XRNJRJgsHukLEnLz6w6SvxZoLgbr5wLqQ==

"@types/nodemailer@^6.2.2":
version "6.2.2"
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.2.2.tgz#08df85c22546e9f3f3c86e5635571c5229ab43a3"
integrity sha512-vDbSSe3+bBXYgibKs8duOrH7bhAv1hMHl3Vtdr3KmXZWLfi5WtIg3X6D/+K4SMK1RbNlm5QZBQCOldST/sVjjg==
dependencies:
"@types/node" "*"

"@types/oauth@*":
version "0.9.1"
resolved "https://registry.yarnpkg.com/@types/oauth/-/oauth-0.9.1.tgz#e17221e7f7936b0459ae7d006255dff61adca305"
Expand Down Expand Up @@ -742,6 +756,14 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"

cron-parser@^2.7.3:
version "2.13.0"
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.13.0.tgz#6f930bb6f2931790d2a9eec83b3ec276e27a6725"
integrity sha512-UWeIpnRb0eyoWPVk+pD3TDpNx3KCFQeezO224oJIkktBrcW6RoAPOx5zIKprZGfk6vcYSmA8yQXItejSaDBhbQ==
dependencies:
is-nan "^1.2.1"
moment-timezone "^0.5.25"

cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
Expand Down Expand Up @@ -824,7 +846,7 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=

define-properties@^1.1.2, define-properties@^1.1.3:
define-properties@^1.1.1, define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
Expand Down Expand Up @@ -1758,6 +1780,13 @@ is-glob@^4.0.0, is-glob@^4.0.1:
dependencies:
is-extglob "^2.1.1"

is-nan@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.2.1.tgz#9faf65b6fb6db24b7f5c0628475ea71f988401e2"
integrity sha1-n69ltvttskt/XAYoR16nH5iEAeI=
dependencies:
define-properties "^1.1.1"

is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
Expand Down Expand Up @@ -1960,6 +1989,11 @@ logform@^2.1.1:
ms "^2.1.1"
triple-beam "^1.3.0"

[email protected]:
version "0.1.1"
resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514"
integrity sha1-lyHXiLR+C8taJMLivuGg2lXatRQ=

lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
Expand Down Expand Up @@ -2048,7 +2082,14 @@ module-alias@^2.2.2:
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==

moment@^2.24.0:
moment-timezone@^0.5.25:
version "0.5.27"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.27.tgz#73adec8139b6fe30452e78f210f27b1f346b8877"
integrity sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==
dependencies:
moment ">= 2.9.0"

"moment@>= 2.9.0", moment@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
Expand Down Expand Up @@ -2147,6 +2188,20 @@ [email protected]:
resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f"
integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==

node-schedule@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/node-schedule/-/node-schedule-1.3.2.tgz#d774b383e2a6f6ade59eecc62254aea07cd758cb"
integrity sha512-GIND2pHMHiReSZSvS6dpZcDH7pGPGFfWBIEud6S00Q8zEIzAs9ommdyRK1ZbQt8y1LyZsJYZgPnyi7gpU2lcdw==
dependencies:
cron-parser "^2.7.3"
long-timeout "0.1.1"
sorted-array-functions "^1.0.0"

nodemailer@^6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.3.1.tgz#2784beebac6b9f014c424c54dbdcc5c4d1221346"
integrity sha512-j0BsSyaMlyadEDEypK/F+xlne2K5m6wzPYMXS/yxKI0s7jmT1kBx6GEKRVbZmyYfKOsjkeC/TiMVDJBI/w5gMQ==

normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
Expand Down Expand Up @@ -2777,6 +2832,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=

sorted-array-functions@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz#43265b21d6e985b7df31621b1c11cc68d8efc7c3"
integrity sha512-sWpjPhIZJtqO77GN+LD8dDsDKcWZ9GCOJNqKzi1tvtjGIzwfoyuRH8S0psunmc6Z5P+qfDqztSbwYR5X/e1UTg==

source-map-support@^0.5.6:
version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
Expand Down

0 comments on commit 77f0967

Please sign in to comment.