-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from kuukuuuruyuk/develop
Develop modif besar, banyak fix
- Loading branch information
Showing
55 changed files
with
1,034 additions
and
444 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ RABBITMQ_SERVER= | |
UPLOADS_DIRECTORY= | ||
|
||
MAIL_HOST= | ||
MAil_PORT= | ||
MAIL_PORT= | ||
MAIL_ADDRESS= | ||
MAIL_PASSWORD= | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,3 @@ node_modules/ | |
package-lock.json | ||
|
||
/.vscode | ||
|
||
consumer/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
require('dotenv').config(); | ||
|
||
const {Pool} = require('pg'); | ||
const amqp = require('amqplib'); | ||
const redis = require('redis'); | ||
const nodemailer = require('nodemailer'); | ||
// Service | ||
const {PlayistsService} = require('./src/playists-service'); | ||
const {MailSender} = require('./src/mail-sender'); | ||
const {Listener} = require('./src/listener'); | ||
const {CacheControl} = require('./src/cache/cache-control'); | ||
const {InvariantError} = require('./src/exception/invariant-error'); | ||
|
||
const initConsumer = async () => { | ||
const dbPool = new Pool(); | ||
const redisConfig = () => { | ||
const client = redis.createClient({ | ||
socket: { | ||
host: process.env.REDIS_SERVER_HOST, | ||
}, | ||
}); | ||
|
||
client.on('error', (error) => { | ||
throw new InvariantError(error); | ||
}); | ||
|
||
client.connect(); | ||
|
||
return client; | ||
}; | ||
const cacheControl = new CacheControl({redis: redisConfig()}); | ||
const playistsService = new PlayistsService(dbPool, {cacheControl}); | ||
const mailSender = new MailSender({ | ||
transporter: nodemailer.createTransport({ | ||
host: process.env.MAIL_HOST, | ||
port: process.env.MAIL_PORT, | ||
auth: { | ||
user: process.env.MAIL_ADDRESS, | ||
pass: process.env.MAIL_PASSWORD, | ||
}, | ||
}), | ||
}); | ||
const listener = new Listener({playistsService, mailSender}); | ||
|
||
const initRabbitMQ = async () => { | ||
const connection = await amqp.connect(process.env.RABBITMQ_SERVER); | ||
const channel = await connection.createChannel(); | ||
|
||
await channel.assertQueue('export:playlists', {durable: true}); | ||
|
||
channel.consume('export:playlists', listener.eventListener, {noAck: true}); | ||
}; | ||
|
||
initRabbitMQ(); | ||
}; | ||
|
||
initConsumer(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* Redis cache control | ||
*/ | ||
class CacheControl { | ||
/** | ||
* Cache redis control | ||
* | ||
* @param {any} service Cache service | ||
*/ | ||
constructor(service) { | ||
this._client = service.redis; | ||
} | ||
|
||
/** | ||
* Set redis cache | ||
* | ||
* @param {string} key Key cache | ||
* @param {string} value Value cache | ||
* @param {number} expInSec Expire | ||
*/ | ||
async set(key, value, expInSec = 900) { | ||
await this._client.set(key, value, { | ||
EX: expInSec, | ||
}); | ||
} | ||
|
||
/** | ||
* Get redis cache by key | ||
* | ||
* @param {string} key Cache key | ||
*/ | ||
async get(key) { | ||
const result = await this._client.get(key); | ||
if (!result) throw new Error('Cache not found'); | ||
} | ||
|
||
/** | ||
* Hapus cache data | ||
* | ||
* @param {string} key Cache redis key | ||
* @return {any} Cache data | ||
*/ | ||
del(key) { | ||
return this._client.del(key); | ||
} | ||
} | ||
|
||
module.exports = {CacheControl}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Client exception | ||
*/ | ||
class ClientError extends Error { | ||
/** | ||
* Client error | ||
* | ||
* @param {string} message Error message | ||
* @param {number} statusCode Error code | ||
*/ | ||
constructor(message, statusCode = 400) { | ||
super(message); | ||
this.name = 'Client Error'; | ||
this.statusCode = statusCode; | ||
} | ||
} | ||
|
||
module.exports = {ClientError}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const {ClientError} = require('./client-error'); | ||
|
||
/** | ||
* Invariant exception | ||
*/ | ||
class InvariantError extends ClientError { | ||
/** | ||
* Invariant error | ||
* | ||
* @param {string} message Error message | ||
*/ | ||
constructor(message) { | ||
super(message); | ||
this.name = 'Invariant Error'; | ||
} | ||
} | ||
|
||
module.exports = {InvariantError}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const {ClientError} = require('./client-error'); | ||
|
||
/** | ||
* Not found exception | ||
*/ | ||
class NotFoundError extends ClientError { | ||
/** | ||
* Not founde error | ||
* | ||
* @param {string} message Error message | ||
*/ | ||
constructor(message) { | ||
super(message, 404); | ||
this.name = 'Not Found Error'; | ||
} | ||
} | ||
|
||
module.exports = {NotFoundError}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/** | ||
* Listener | ||
*/ | ||
class Listener { | ||
/** | ||
* Listener | ||
* | ||
* @param {any} service Playlist servis | ||
*/ | ||
constructor(service) { | ||
this._playistsService = service.playistsService; | ||
this._mailSender = service.mailSender; | ||
|
||
this.eventListener = this.eventListener.bind(this); | ||
} | ||
|
||
/** | ||
* Evant listener | ||
* | ||
* @param {string} message Message content | ||
*/ | ||
async eventListener(message) { | ||
try { | ||
const {playlistId, targetEmail} = JSON.parse(message.content.toString()); | ||
const songs = | ||
await this._playistsService.getSongsFromPlaylistId(playlistId); | ||
const playlist = await this._playistsService.getPlaylistById(playlistId); | ||
await this._mailSender.sendEmail(targetEmail, JSON.stringify({ | ||
playlist: { | ||
...playlist, | ||
songs, | ||
}, | ||
})); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
} | ||
} | ||
|
||
module.exports = {Listener}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* Mail sender | ||
*/ | ||
class MailSender { | ||
/** | ||
* Mail sender | ||
* | ||
* @param {any} service Service mail sender | ||
*/ | ||
constructor(service) { | ||
this._transporter = service.transporter; | ||
} | ||
|
||
/** | ||
* send email | ||
* | ||
* @param {string} targetEmail Email target | ||
* @param {any} content Emai content | ||
* @return {any} Info sender | ||
*/ | ||
async sendEmail(targetEmail, content) { | ||
const message = { | ||
from: 'Openmusic API', | ||
to: targetEmail, | ||
subject: 'Ekspor Playlists', | ||
text: 'Export Playlists Data', | ||
attachments: [ | ||
{ | ||
filename: 'playlists.json', | ||
content, | ||
}, | ||
], | ||
}; | ||
|
||
return await this._transporter.sendMail(message); | ||
} | ||
} | ||
|
||
module.exports = {MailSender}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
const {NotFoundError} = require('./exception/not-found-error'); | ||
|
||
/** | ||
* Playlist service | ||
*/ | ||
class PlayistsService { | ||
/** | ||
* Playlist Service | ||
* | ||
* @param {any} db Database pool | ||
* @param {any} service Playlist service | ||
*/ | ||
constructor(db, service) { | ||
this._pool = db; | ||
this._cacheControl = service.cacheControl; | ||
} | ||
|
||
/** | ||
* Get playlist data | ||
* | ||
* @param {string} playlistId Playlist id | ||
* @return {any} Playlist model | ||
*/ | ||
async getPlaylistById(playlistId) { | ||
const sql = 'SELECT id, name FROM playlists WHERE id=$1'; | ||
const result = await this._pool.query(sql, [playlistId]); | ||
|
||
if (!result.rowCount) { | ||
throw new NotFoundError('Failed, playlist ID not found!'); | ||
} | ||
|
||
return result.rows[0]; | ||
} | ||
|
||
/** | ||
* Find all playlist | ||
* | ||
* @param {string} playlistId Playlist id | ||
* @return {any} Playlist model | ||
*/ | ||
async getSongsFromPlaylistId(playlistId) { | ||
try { | ||
const result = await this._cacheControl.get(`playlist:${playlistId}`); | ||
|
||
return JSON.parse(result); | ||
} catch { | ||
const sql = [ | ||
'SELECT songs.id, songs.title, songs.performer', | ||
'FROM songs', | ||
'LEFT JOIN playlist_songs ON songs.id = playlist_songs.song_id', | ||
'WHERE playlist_songs.playlist_id=$1', | ||
].join(' '); | ||
const result = await this._pool.query(sql, [playlistId]); | ||
const parseToJSON = JSON.stringify(result.rows); | ||
|
||
await this._cacheControl.set(`playlist:${playlistId}`, parseToJSON); | ||
|
||
return result.rows; | ||
} | ||
} | ||
} | ||
|
||
module.exports = {PlayistsService}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
/* eslint-disable camelcase */ | ||
|
||
exports.up = (pgm) => { | ||
pgm.addColumn('albums', { | ||
pgm.addColumns('albums', { | ||
cover: {type: 'text'}, | ||
}); | ||
}; | ||
|
||
exports.down = (pgm) => { | ||
pgm.dropColumn('albums', 'cover'); | ||
pgm.dropColumns('albums', 'cover'); | ||
}; |
18 changes: 18 additions & 0 deletions
18
migrations/1664446606892_add-created-at-updated-at-to-songs.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* eslint-disable camelcase */ | ||
|
||
exports.up = (pgm) => { | ||
pgm.addColumns('songs', { | ||
created_at: { | ||
type: 'timestamp', | ||
notNull: true, | ||
default: pgm.func('current_timestamp'), | ||
}, | ||
updated_at: { | ||
type: 'timestamp', | ||
}, | ||
}); | ||
}; | ||
|
||
exports.down = (pgm) => { | ||
pgm.dropColumns('songs', ['created_at', 'updated_at']); | ||
}; |
18 changes: 18 additions & 0 deletions
18
migrations/1664447037930_add-created-at-updated-at-to-albums.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* eslint-disable camelcase */ | ||
|
||
exports.up = (pgm) => { | ||
pgm.addColumns('albums', { | ||
created_at: { | ||
type: 'timestamp', | ||
notNull: true, | ||
default: pgm.func('current_timestamp'), | ||
}, | ||
updated_at: { | ||
type: 'timestamp', | ||
}, | ||
}); | ||
}; | ||
|
||
exports.down = (pgm) => { | ||
pgm.dropColumns('songs', ['created_at', 'updated_at']); | ||
}; |
Oops, something went wrong.