Skip to content

Commit

Permalink
feat(config): add a config service working and Sentry support in prod
Browse files Browse the repository at this point in the history
  • Loading branch information
Chazz committed Dec 15, 2019
1 parent c47d770 commit 0791984
Show file tree
Hide file tree
Showing 17 changed files with 116 additions and 61 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ typings/
# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

release
dist
nuclear.json
Expand Down
3 changes: 3 additions & 0 deletions packages/app/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ACOUSTIC_ID_KEY=Fivodjxo37
# not used in development i put it here to not forget to add it in travis or whaterver CI we use
SENTRY_DSN=https://2fb5587831994721a8b5f77bf6010679@sentry.io/1256142
21 changes: 13 additions & 8 deletions packages/app/app/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'regenerator-runtime';

import React from 'react';
import ReactDOM from 'react-dom';
import { MemoryRouter } from 'react-router-dom';
Expand All @@ -7,22 +8,26 @@ import { AppContainer } from 'react-hot-loader';
import { I18nextProvider } from 'react-i18next';
import logger from 'electron-timber';
import Img from 'react-image-smooth-loading';
import * as Sentry from '@sentry/electron';

import artPlaceholder from '../resources/media/art_placeholder.png';
import i18n, { setupI18n } from './i18n';
import App from './App';
import configureStore from './store/configureStore';

const store = configureStore();
logger.hookConsole({
renderer: true
});
window.store = store; // put store in global scope for plugins
if (process.env.NODE_ENV === 'production') {
process.env.SENTRY_DSN && Sentry.init({
dsn: process.env.SENTRY_DSN
});
} else {
logger.hookConsole({
renderer: true
});
}

// Sentry
process.env.NODE_ENV === 'production' &&
Raven.config('https://2fb5587831994721a8b5f77bf6010679@sentry.io/1256142').install();

const store = configureStore();
window.store = store; // put store in global scope for plugins
// Global image placeholder
Img.globalPlaceholder = artPlaceholder;

Expand Down
4 changes: 0 additions & 4 deletions packages/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
<meta charset="UTF-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css"></link>
<% if (htmlWebpackPlugin.options.production) { %>
<!-- Sentry.io error monitoring -->
<script src="https://cdn.ravenjs.com/3.26.4/raven.min.js" crossorigin="anonymous"></script>
<% } %>
<style>
body {
background: #282a36;
Expand Down
4 changes: 3 additions & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"electron:prod": "npm run build:electron && electron ./dist/bundle.electron.js",
"electron:docker": "docker run --rm --net=host --env=\"DISPLAY\" --volume=\"$HOME/.Xauthority:/root/.Xauthority:rw\" --device /dev/snd nuclear",
"watch": "webpack-dev-server --verbose --inline --env.NODE_ENV=development",
"build:dist": "webpack --colors --env.NODE_ENV=production",
"build:dist": "webpack --colors --progress --env.NODE_ENV=production",
"build:electron": "webpack --colors --env.NODE_ENV=production --config=webpack.config.electron.js",
"build:docker": "docker build -t nuclear .",
"test": "mocha --require ./test/testHelper.js --require regenerator-runtime --timeout 10000 --prof --recursive",
Expand Down Expand Up @@ -43,6 +43,7 @@
"dependencies": {
"@nuclear/core": "0.5.1",
"@nuclear/ui": "0.5.1",
"@sentry/electron": "^1.0.0",
"@sentry/node": "^5.10.1",
"awilix": "^4.2.3",
"billboard-top-100": "^2.0.8",
Expand All @@ -54,6 +55,7 @@
"cors": "^2.8.5",
"d3-drag": "^1.2.3",
"d3-selection": "^1.4.0",
"dotenv": "^8.2.0",
"electron-dl": "^1.14.0",
"electron-platform": "^1.2.0",
"electron-store": "^2.0.0",
Expand Down
5 changes: 5 additions & 0 deletions packages/app/server/main.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import installExtension, {
} from 'electron-devtools-installer';
import getPort from 'get-port';
import url from 'url';
import dotenv from 'dotenv';

import DownloadIpcCtrl from './ipc/download';
import LocalLibraryIpcCtrl from './ipc/localLibrary';
Expand All @@ -25,10 +26,14 @@ import Store from './services/store';
import Window from './services/window';
import * as platform from './services/platform';
import Container from './helpers/container';
import Config from './services/config';

dotenv.config();

let container;
const services = [
{ provide: 'acousticId', useClass: AcousticId },
{ provide: 'config', useClass: Config },
{ provide: 'download', useClass: Download },
{ provide: 'httpApi', useClass: HttpApi },
{ provide: 'localLibrary', useClass: LocalLibrary },
Expand Down
9 changes: 9 additions & 0 deletions packages/app/server/main.prod.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'regenerator-runtime';

import { transformSource } from '@nuclear/core';
import * as Sentry from '@sentry/electron/dist/main';
import { app, ipcMain } from 'electron';
import path from 'path';
import url from 'url';
Expand All @@ -21,10 +22,16 @@ import Window from './services/window';
import logger from './services/loggerProd';
import * as platform from './services/platform';
import Container from './helpers/container';
import Config from './services/config';

// TOTO remove this when en variables are on CI
process.env.SENTRY_DSN='https://2fb5587831994721a8b5f77bf6010679@sentry.io/1256142';
process.env.ACOUSTIC_ID_KEY = 'Fivodjxo37';

let container;
const services = [
{ provide: 'acousticId', useClass: AcousticId },
{ provide: 'config', useClass: Config },
{ provide: 'download', useClass: Download },
{ provide: 'httpApi', useClass: HttpApi },
{ provide: 'localLibrary', useClass: LocalLibrary },
Expand All @@ -50,7 +57,9 @@ app.on('ready', () => {
app.transformSource = transformSource;
container = new Container({ ipcControllers, services }, { ipc: ipcMain });
const window = container.resolve('window');
const config = container.resolve('config');

Sentry.init({ dsn: config.sentryDsn });
container.ipcListen();

window.loadURL(url.format({
Expand Down
11 changes: 5 additions & 6 deletions packages/app/server/services/acousticId.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ import filter from 'stream-filter';
import reduce from 'stream-reduce';
import path from 'path';

const API_KEY = 'Fivodjxo37';
const API_URL = 'https://api.acoustid.org/v2/lookup';

/**
* Generate audio fingerprint with fpcalc
* Send fingerprint to acousticId and get metadata
* @see {@link https://acoustid.org/}
* @see {@link https://helpmanual.io/help/fpcalc/}
*/
class AcousticId {
constructor({ platform }) {
constructor({ config, platform }) {
/** @type {import('./config').default} */
this.config = config;
/** @type {import('./platform')} */
this.platform = platform;
}
Expand All @@ -31,12 +30,12 @@ class AcousticId {
const query = {
format: 'json',
meta: 'recordings',
client: API_KEY,
client: this.config.acousticId.key,
duration,
fingerprint
};

const res = await fetch(`${API_URL}?${stringify(query)}
const res = await fetch(`${this.config.acousticId.url}?${stringify(query)}
`);

return res.json();
Expand Down
42 changes: 42 additions & 0 deletions packages/app/server/services/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const MANDATORY_ENV = ['ACOUSTIC_ID_KEY'];

class Config {
constructor() {
if (process.env.NODE_ENV === 'production') {
MANDATORY_ENV.push('SENTRY_DSN');
}

this._validateEnv();

/** @type {string} */
this.sentryDsn = process.env.SENTRY_DSN;
/** @type {{ key: string, url: string }} */
this.acousticId = {
key: process.env.ACOUSTIC_ID_KEY,
url: 'https://api.acoustid.org/v2/lookup'
};
/** @type {string} */
this.youtubeUrl = 'https://www.youtube.com/watch';
/** @type {string} */
this.title = 'Nuclear Music Player';
/** @type {string[]} */
this.supportedFormats = [
'aac',
'flac',
'm4a',
'mp3',
'ogg',
'wav'
];
}

_validateEnv() {
MANDATORY_ENV.forEach(ENV => {
if (!process.env[ENV]) {
throw new Error(`missing mandatory env variable ${ENV}`);
}
});
}
}

export default Config;
6 changes: 4 additions & 2 deletions packages/app/server/services/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ import _ from 'lodash';
* @see {@link https://github.com/sindresorhus/electron-dl}
*/
class Download {
constructor({ window, youtubeSearch, store }) {
constructor({ config, window, youtubeSearch, store }) {
electronDl();
/** @type {import('./window').default} */
this.window = window;
/** @type {(query: any) => Promise} */
this.youtubeSearch = youtubeSearch;
/** @type {import('./store').default} */
this.store = store;
/** @type {import('./config').default} */
this.config = config;
}

/**
Expand All @@ -40,7 +42,7 @@ class Download {
const response = await this.youtubeSearch(query);
const ytData = await response.json();
const trackId = _.get(_.head(ytData.items), 'id.videoId');
const videoInfo = await ytdl.getInfo(`https://www.youtube.com/watch?v=${trackId}`);
const videoInfo = await ytdl.getInfo(`${this.config.youtubeUrl}?v=${trackId}`);
const formatInfo = _.head(videoInfo.formats.filter(e => e.itag === 140));
const streamUrl = formatInfo.url;

Expand Down
3 changes: 1 addition & 2 deletions packages/app/server/services/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
import { errorMiddleware, notFoundMiddleware } from '../http/middlewares';
import { initSwagger } from '../http/swagger';

const HOST = '0.0.0.0';
const PREFIX = '/nuclear';

/**
Expand Down Expand Up @@ -57,7 +56,7 @@ class HttpApi {
.use(`${PREFIX}/equalizer`, equalizerRouter(this.store, this.rendererWindow))
.use(notFoundMiddleware())
.use(errorMiddleware(this.logger))
.listen(port, HOST, err => {
.listen(port, '0.0.0.0', err => {
if (err) {
this.logger.error('Something fail during http api initialisation');
this.logger.error(err);
Expand Down
15 changes: 4 additions & 11 deletions packages/app/server/services/localLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ import path from 'path';
import { promisify } from 'util';
import uuid from 'uuid/v4';

const SUPPORTED_FORMATS = [
'aac',
'flac',
'm4a',
'mp3',
'ogg',
'wav'
];

/**
* @typedef {Object} Image
* @property {string} #text
Expand All @@ -40,7 +31,9 @@ const SUPPORTED_FORMATS = [
* format all these metadata the nuclear way and store it in a memory cache
*/
class LocalLibrary {
constructor({ store, acousticId, logger }) {
constructor({ config, store, acousticId, logger }) {
/** @type {import('./config').default} */
this.config = config;
/** @type {import('./store').default} */
this.store = store;
/** @type {import('./acousticId').default} */
Expand Down Expand Up @@ -158,7 +151,7 @@ class LocalLibrary {
const directories = this.store.get('localFolders');
const baseFiles = await Promise.all(
_.flatMap(
SUPPORTED_FORMATS,
this.config.supportedFormats,
format => directories.map(
dir => promisify(glob)(`${dir}/**/*.${format}`)
)
Expand Down
7 changes: 6 additions & 1 deletion packages/app/server/services/loggerProd.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import * as Sentry from '@sentry/electron';

const logger = {
log() {},
error() {}
warn() {},
error(err) {
Sentry.captureException(err);
}
};

export default logger;
14 changes: 4 additions & 10 deletions packages/app/server/services/mpris.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,12 @@ const loopStatusMapper = {
* @see {@link https://github.com/altdesktop/playerctl}
*/
class MprisPlayer extends Player {
constructor({ ipc, window, store, mprisLogger }) {
constructor({ config, ipc, window, store, mprisLogger }) {
super({
name: 'Nuclear',
identity: 'Nuclear Music Player',
name: config.title.replace(/ /g, '_'),
identity: config.title,
supportedMimeTypes: config.supportedFormats.map(format => `audio/${format}`),
supportedUriSchemes: ['file', 'uri'],
supportedMimeTypes: [
'audio/mpeg',
'audio/acc',
'audio/x-flac',
'audio/wav',
'audio/ogg'
],
supportedInterfaces: ['player', 'trackList', 'playlists']
});

Expand Down
17 changes: 9 additions & 8 deletions packages/app/server/services/window.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import path from 'path';
class Window extends BrowserWindow {
/**
* @param {{
* httpApi: import('./http').default,
* platform: import('./platform'),
* store: import('./store').default
* }} param0
* config: import('./config').default,
* httpApi: import('./http').default,
* platform: import('./platform'),
* store: import('./store').default
* }} param0
*/
constructor({ httpApi, platform, store }) {
constructor({ config, httpApi, platform, store }) {
let icon = nativeImage.createFromPath(path.resolve(__dirname, 'resources', 'media', 'icon.png'));
super({
title: 'Nuclear Music Player',
title: config.title,
width: 1366,
height: 768,
frame: !store.getOption('framelessWindow'),
Expand Down Expand Up @@ -51,8 +52,8 @@ class Window extends BrowserWindow {
]);

const tray = new Tray(icon);
tray.setTitle('Nuclear Music Player');
tray.setToolTip('Nuclear Music Player');
tray.setTitle(config.title);
tray.setToolTip(config.title);
tray.setContextMenu(trayMenu);
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/app/webpack.config.electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ module.exports = (env) => {
filename: IS_PROD ? './dist/bundle.electron.js' : './bundle.electron.js'
},
mode: IS_PROD ? 'production' : 'development',
stats: 'errors-only',
// stats: 'minimal'
// stats: 'errors-only',
stats: 'minimal',
optimization: { namedModules: true },
module: {
rules: [jsxRule]
Expand Down
Loading

0 comments on commit 0791984

Please sign in to comment.