├── src ├── images │ ├── pc.png │ ├── 3do.png │ ├── amiga.png │ ├── c64.png │ ├── coco.png │ ├── mame.png │ ├── msx.png │ ├── n64.png │ ├── nds.png │ ├── nes.png │ ├── ngp.png │ ├── psp.png │ ├── psx.png │ ├── snes.png │ ├── tg16.png │ ├── amstrad.png │ ├── appleII.png │ ├── atarist.png │ ├── gameboy.png │ ├── genesis.png │ ├── neogeo.png │ ├── sega32x.png │ ├── segacd.png │ ├── sg-1000.png │ ├── vectrex.png │ ├── atari2600.png │ ├── atari5200.png │ ├── atari7800.png │ ├── atarilynx.png │ ├── dragon32.png │ ├── dreamcast.png │ ├── gamegear.png │ ├── macintosh.png │ ├── ngp_black.png │ ├── pcengine.png │ ├── samcoupe.png │ ├── videopac.png │ ├── virtualboy.png │ ├── wonderswan.png │ ├── zxspectrum.png │ ├── atarijaguar.png │ ├── mastersystem.png │ ├── gameboyadvance.png │ ├── gameboycolour.png │ ├── intellivision.png │ └── wonderswan-black.png ├── public │ ├── tile.png │ ├── favicon.ico │ ├── tile-wide.png │ ├── apple-touch-icon.png │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── robots.txt │ ├── vendors.css │ ├── humans.txt │ ├── browserconfig.xml │ ├── crossdomain.xml │ └── css │ │ └── bootstrap-theme.min.css ├── config │ ├── retropie.dev.json │ ├── retropie.json │ ├── README.md │ └── systems.json ├── models │ ├── Temp.js │ ├── Memory.js │ ├── Disk.js │ └── CPU.js ├── components │ ├── Link │ │ ├── package.json │ │ └── Link.js │ ├── Footer │ │ ├── package.json │ │ ├── Footer.js │ │ └── Footer.css │ ├── Header │ │ ├── RetroPie_Logo-60x60.png │ │ ├── package.json │ │ ├── Header.js │ │ └── Header.css │ ├── Image │ │ ├── no_image_available.png │ │ ├── package.json │ │ └── Image.js │ ├── Layout │ │ ├── package.json │ │ ├── Layout.js │ │ ├── Layout.test.js │ │ └── Layout.css │ ├── MiniStats │ │ ├── package.json │ │ ├── MiniStats.css │ │ └── MiniStats.js │ ├── Navigation │ │ ├── package.json │ │ ├── Navigation.css │ │ └── Navigation.js │ ├── ProgressBar │ │ ├── package.json │ │ ├── progressbar.css │ │ └── ProgressBar.js │ ├── SystemsList │ │ ├── package.json │ │ ├── SystemsList.css │ │ └── SystemsList.js │ ├── Upload │ │ ├── Upload.css │ │ └── index.js │ ├── variables.css │ ├── SystemHealth.js │ ├── Html.js │ └── App.js ├── core │ ├── fetch │ │ ├── package.json │ │ ├── fetch.client.js │ │ └── fetch.server.js │ └── history.js ├── store │ ├── logger │ │ ├── package.json │ │ ├── logger.client.js │ │ └── logger.server.js │ ├── createHelpers.js │ └── configureStore.js ├── helpers │ ├── memory.js │ ├── index.js │ └── cpu.js ├── reducers │ ├── index.js │ ├── upload.js │ ├── check.js │ ├── stats.js │ └── list.js ├── workers │ ├── index.js │ ├── Check.js │ ├── Stats.js │ ├── Temp.js │ ├── Upload.js │ ├── Delete.js │ └── List.js ├── config.js ├── actions │ ├── stats.js │ ├── upload.js │ ├── list.js │ └── check.js ├── routes │ ├── stats-board │ │ ├── stats-board.css │ │ ├── index.js │ │ └── StatsBoard.js │ ├── home │ │ ├── index.js │ │ ├── Home.css │ │ └── Home.js │ ├── notFound │ │ ├── NotFound.css │ │ ├── index.js │ │ └── NotFound.js │ ├── systems │ │ ├── index.js │ │ ├── systems.css │ │ └── Systems.js │ ├── error │ │ ├── index.js │ │ ├── ErrorPage.css │ │ └── ErrorPage.js │ ├── system │ │ ├── system.css │ │ ├── index.js │ │ └── System.js │ └── index.js ├── worker.js ├── constants │ └── index.js ├── server.js └── client.js ├── tools ├── .eslintrc ├── lib │ ├── fetch.js │ └── fs.js ├── clean.js ├── bundle.js ├── build.js ├── retropie-web-gui.sh ├── copy.js ├── run.js ├── render.js ├── README.md ├── runServer.js ├── deploy.js ├── start.js ├── install.sh └── webpack.config.js ├── .flowconfig ├── test ├── .eslintrc └── setup.js ├── .travis.yml ├── .editorconfig ├── .gitattributes ├── LICENSE ├── ReactStarterKit.LICENSE.txt ├── .gitignore ├── README.md └── package.json /src/images/pc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/pc.png -------------------------------------------------------------------------------- /src/images/3do.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/3do.png -------------------------------------------------------------------------------- /src/images/amiga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/amiga.png -------------------------------------------------------------------------------- /src/images/c64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/c64.png -------------------------------------------------------------------------------- /src/images/coco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/coco.png -------------------------------------------------------------------------------- /src/images/mame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/mame.png -------------------------------------------------------------------------------- /src/images/msx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/msx.png -------------------------------------------------------------------------------- /src/images/n64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/n64.png -------------------------------------------------------------------------------- /src/images/nds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/nds.png -------------------------------------------------------------------------------- /src/images/nes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/nes.png -------------------------------------------------------------------------------- /src/images/ngp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/ngp.png -------------------------------------------------------------------------------- /src/images/psp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/psp.png -------------------------------------------------------------------------------- /src/images/psx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/psx.png -------------------------------------------------------------------------------- /src/images/snes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/snes.png -------------------------------------------------------------------------------- /src/images/tg16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/tg16.png -------------------------------------------------------------------------------- /src/public/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/tile.png -------------------------------------------------------------------------------- /src/images/amstrad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/amstrad.png -------------------------------------------------------------------------------- /src/images/appleII.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/appleII.png -------------------------------------------------------------------------------- /src/images/atarist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/atarist.png -------------------------------------------------------------------------------- /src/images/gameboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/gameboy.png -------------------------------------------------------------------------------- /src/images/genesis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/genesis.png -------------------------------------------------------------------------------- /src/images/neogeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/neogeo.png -------------------------------------------------------------------------------- /src/images/sega32x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/sega32x.png -------------------------------------------------------------------------------- /src/images/segacd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/segacd.png -------------------------------------------------------------------------------- /src/images/sg-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/sg-1000.png -------------------------------------------------------------------------------- /src/images/vectrex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/vectrex.png -------------------------------------------------------------------------------- /src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/favicon.ico -------------------------------------------------------------------------------- /src/images/atari2600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/atari2600.png -------------------------------------------------------------------------------- /src/images/atari5200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/atari5200.png -------------------------------------------------------------------------------- /src/images/atari7800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/atari7800.png -------------------------------------------------------------------------------- /src/images/atarilynx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/atarilynx.png -------------------------------------------------------------------------------- /src/images/dragon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/dragon32.png -------------------------------------------------------------------------------- /src/images/dreamcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/dreamcast.png -------------------------------------------------------------------------------- /src/images/gamegear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/gamegear.png -------------------------------------------------------------------------------- /src/images/macintosh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/macintosh.png -------------------------------------------------------------------------------- /src/images/ngp_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/ngp_black.png -------------------------------------------------------------------------------- /src/images/pcengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/pcengine.png -------------------------------------------------------------------------------- /src/images/samcoupe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/samcoupe.png -------------------------------------------------------------------------------- /src/images/videopac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/videopac.png -------------------------------------------------------------------------------- /src/images/virtualboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/virtualboy.png -------------------------------------------------------------------------------- /src/images/wonderswan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/wonderswan.png -------------------------------------------------------------------------------- /src/images/zxspectrum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/zxspectrum.png -------------------------------------------------------------------------------- /src/public/tile-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/tile-wide.png -------------------------------------------------------------------------------- /tools/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": 0, 4 | "global-require": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/build 3 | .*/config 4 | .*/node_modules 5 | .*/gulpfile.js 6 | 7 | [include] 8 | -------------------------------------------------------------------------------- /src/images/atarijaguar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/atarijaguar.png -------------------------------------------------------------------------------- /src/images/mastersystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/mastersystem.png -------------------------------------------------------------------------------- /src/images/gameboyadvance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/gameboyadvance.png -------------------------------------------------------------------------------- /src/images/gameboycolour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/gameboycolour.png -------------------------------------------------------------------------------- /src/images/intellivision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/intellivision.png -------------------------------------------------------------------------------- /src/config/retropie.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": "tmp", 3 | "temp_cmd": "cat /sys/class/thermal/thermal_zone0/temp" 4 | } 5 | -------------------------------------------------------------------------------- /src/images/wonderswan-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/images/wonderswan-black.png -------------------------------------------------------------------------------- /src/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/apple-touch-icon.png -------------------------------------------------------------------------------- /src/public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /src/config/retropie.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": "/home/pi/RetroPie", 3 | "temp_cmd": "cat /sys/class/thermal/thermal_zone0/temp" 4 | } 5 | -------------------------------------------------------------------------------- /src/models/Temp.js: -------------------------------------------------------------------------------- 1 | import {Record} from 'immutable'; 2 | 3 | export default Record({ 4 | temp: 0, 5 | error: null, 6 | }); 7 | -------------------------------------------------------------------------------- /src/public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/components/Link/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Link", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Link.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/components/Footer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Footer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Footer.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Header/RetroPie_Logo-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/components/Header/RetroPie_Logo-60x60.png -------------------------------------------------------------------------------- /src/components/Header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Header", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Header.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Image/no_image_available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fechy/retropie-web-gui/HEAD/src/components/Image/no_image_available.png -------------------------------------------------------------------------------- /src/components/Image/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Image", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Image.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Layout", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Layout.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/public/vendors.css: -------------------------------------------------------------------------------- 1 | @import './css/font-awesome.min.css'; 2 | @import './css/bootstrap.min.css'; 3 | @import './css/bootstrap-theme.min.css'; 4 | -------------------------------------------------------------------------------- /src/models/Memory.js: -------------------------------------------------------------------------------- 1 | import {Record} from 'immutable'; 2 | 3 | export default Record({ 4 | free: 0, 5 | total: 0, 6 | percentage: 0, 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/MiniStats/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MiniStats", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./MiniStats.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/core/fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "fetch", 4 | "main": "./fetch.server.js", 5 | "browser": "./fetch.client.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Navigation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Navigation", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Navigation.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/ProgressBar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ProgressBar", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./ProgressBar.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/SystemsList/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SystemsList", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./SystemsList.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "rules": { 6 | "no-unused-expressions": 0, 7 | "padded-blocks": 0 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/models/Disk.js: -------------------------------------------------------------------------------- 1 | import {Record} from 'immutable'; 2 | 3 | export default Record({ 4 | availablePercentage: 100, 5 | available: 0, 6 | free: 0, 7 | total: 0 8 | }); 9 | -------------------------------------------------------------------------------- /src/store/logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "redux-logger-middleware", 4 | "main": "./logger.server.js", 5 | "browser": "./logger.client.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/store/logger/logger.client.js: -------------------------------------------------------------------------------- 1 | import reduxLogger from 'redux-logger'; 2 | 3 | export default function createLogger() { 4 | return reduxLogger({ 5 | collapsed: true, 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /src/models/CPU.js: -------------------------------------------------------------------------------- 1 | import {Record} from 'immutable'; 2 | 3 | export default Record({ 4 | overall: { 5 | idle: 0, 6 | total: 100, 7 | percentage: 0, 8 | }, 9 | all: [], 10 | }); 11 | -------------------------------------------------------------------------------- /src/helpers/memory.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | 3 | export default function () { 4 | const free = os.freemem(); 5 | const total = os.totalmem(); 6 | return { 7 | free, 8 | total, 9 | percentage: (free * 100) / total 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '5' 5 | env: 6 | - CXX=g++-4.8 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - g++-4.8 13 | script: 14 | - npm run lint 15 | - npm run test 16 | -------------------------------------------------------------------------------- /src/public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | CSS3, HTML5, JavaScript 15 | React, Flux, SuperAgent 16 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import upload from './upload'; 4 | import list from './list'; 5 | import check from './check'; 6 | import stats from './stats'; 7 | 8 | export default combineReducers({ 9 | upload, 10 | list, 11 | check, 12 | stats, 13 | }); 14 | -------------------------------------------------------------------------------- /src/components/MiniStats/MiniStats.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: absolute; 3 | top:5px; 4 | right: 10px; 5 | } 6 | 7 | .stat { 8 | padding:0 10px; 9 | transition: all .5s ease-out; 10 | } 11 | 12 | .stat i { 13 | width:15px; 14 | margin-right: 5px; 15 | } 16 | 17 | .high { 18 | color:#FF0000; 19 | } 20 | -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | import systems from '../config/systems.json'; 2 | 3 | export function findSystemById(id) { 4 | const found = systems.filter(system => { 5 | return system.name == id; 6 | }); 7 | 8 | return found ? found[0] : null; 9 | } 10 | 11 | export default { 12 | systems, 13 | findSystemById 14 | } 15 | -------------------------------------------------------------------------------- /src/workers/index.js: -------------------------------------------------------------------------------- 1 | 2 | // Init all workers 3 | export default function (app, env, io) 4 | { 5 | require('./Check').default(app, env, io); 6 | require('./List').default(app, env, io); 7 | require('./Upload').default(app, env, io); 8 | require('./Delete').default(app, env, io); 9 | require('./Stats').default(app, env, io); 10 | require('./Temp').default(app, env, io); 11 | } 12 | -------------------------------------------------------------------------------- /src/config/README.md: -------------------------------------------------------------------------------- 1 | # CONFIG FILES 2 | ## retropie.json 3 | Set here all related to RetroPie config. 4 | Pay attention to `path` which should point to the absolute path to RetroPie 5 | folder. 6 | 7 | ## systems.json 8 | Most likely you don't need to touch this file. 9 | If you have, though, take a look at `path` which should point at the folder 10 | where the roms for this system are located. 11 | -------------------------------------------------------------------------------- /src/components/Image/Image.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const notFoundImage = require('./no_image_available.png'); 4 | 5 | class Image extends Component 6 | { 7 | render() { 8 | const { src } = this.props; 9 | const imageSrc = src ? src : notFoundImage; 10 | return ( 11 | 12 | ); 13 | } 14 | } 15 | 16 | export default Image; 17 | -------------------------------------------------------------------------------- /src/store/logger/logger.server.js: -------------------------------------------------------------------------------- 1 | import { inspect } from 'util'; 2 | 3 | // Server side redux action logger 4 | export default function createLogger() { 5 | return store => next => action => { // eslint-disable-line no-unused-vars 6 | const formattedPayload = inspect(action.payload, { 7 | colors: true, 8 | }); 9 | console.log(` * ${action.type}: ${formattedPayload}`); // eslint-disable-line no-console 10 | return next(action); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-disable max-len */ 11 | 12 | export const port = process.env.PORT || 3000; 13 | export const host = process.env.WEBSITE_HOSTNAME || `localhost:${port}`; 14 | -------------------------------------------------------------------------------- /src/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/actions/stats.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | 3 | import { SYSTEM_STATS, SYSTEM_TEMPERATURE } from '../constants'; 4 | 5 | export function update(data) { 6 | return { 7 | type: SYSTEM_STATS, 8 | payload: new Promise(resolve => { 9 | resolve(data); 10 | }) 11 | } 12 | } 13 | 14 | export function updateTemp(data) { 15 | return { 16 | type: SYSTEM_TEMPERATURE, 17 | payload: new Promise(resolve => { 18 | resolve(data); 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tools/lib/fetch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import http from 'http'; 11 | 12 | export default async (url) => new Promise((resolve, reject) => 13 | http.get(url, res => resolve(res)).on('error', err => reject(err)) 14 | ); 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /src/core/fetch/fetch.client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import 'whatwg-fetch'; 11 | 12 | export default self.fetch.bind(self); 13 | export const Headers = self.Headers; 14 | export const Request = self.Request; 15 | export const Response = self.Response; 16 | -------------------------------------------------------------------------------- /src/routes/stats-board/stats-board.css: -------------------------------------------------------------------------------- 1 | @import '../../components/variables.css'; 2 | 3 | .root { 4 | margin: 0 auto; 5 | max-width: var(--max-content-width); 6 | } 7 | 8 | .container { 9 | margin: 0 auto; 10 | } 11 | 12 | .chartSection { 13 | display: inline-block; 14 | width:45%; 15 | padding:10px; 16 | vertical-align: top; 17 | } 18 | 19 | .chartSection .label { 20 | font-size: 18px; 21 | } 22 | 23 | .chartSection .label i { 24 | margin-right:10px; 25 | } 26 | 27 | .error { 28 | color: #FF0000; 29 | } 30 | -------------------------------------------------------------------------------- /src/core/history.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import createBrowserHistory from 'history/createBrowserHistory'; 11 | 12 | // Navigation manager, e.g. history.push('/home') 13 | // https://github.com/mjackson/history 14 | export default process.env.BROWSER && createBrowserHistory(); 15 | -------------------------------------------------------------------------------- /src/routes/home/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Home from './Home'; 12 | 13 | export default { 14 | 15 | path: '/', 16 | 17 | async action() { 18 | return { 19 | title: 'Home', 20 | component: , 21 | }; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/routes/notFound/NotFound.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | -------------------------------------------------------------------------------- /tools/clean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import del from 'del'; 11 | import fs from './lib/fs'; 12 | 13 | /** 14 | * Cleans up the output (build) directory. 15 | */ 16 | async function clean() { 17 | await del(['.tmp', 'build/*', '!build/.git'], { dot: true }); 18 | await fs.makeDir('build/public'); 19 | } 20 | 21 | export default clean; 22 | -------------------------------------------------------------------------------- /src/worker.js: -------------------------------------------------------------------------------- 1 | import init from './workers/index'; 2 | 3 | export default function buildWorker(app, env, io) { 4 | 5 | // Add headers 6 | app.use((req, res, next) => { 7 | res.header('Access-Control-Allow-Origin', '*'); 8 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 9 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); 10 | 11 | next(); 12 | }); 13 | 14 | // Set the api root 15 | app.get('/api', () => { 16 | throw new Error('invalid access'); 17 | }); 18 | 19 | // Init all workers 20 | init(app, env, io); 21 | } 22 | -------------------------------------------------------------------------------- /src/routes/notFound/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import NotFound from './NotFound'; 12 | 13 | const title = 'Page Not Found'; 14 | 15 | export default { 16 | 17 | path: '*', 18 | 19 | action() { 20 | return { 21 | title, 22 | component: , 23 | status: 404, 24 | }; 25 | }, 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/ProgressBar/progressbar.css: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 30px; 3 | border: 2px solid #B2BABB; 4 | background: #EBF5FB; 5 | overflow: hidden; 6 | -webkit-border-radius: 3px; 7 | -moz-border-radius: 3px; 8 | border-radius: 3px; 9 | } 10 | 11 | .bar { 12 | background: #F5B041; 13 | padding: 2px 5px; 14 | height: 100%; 15 | text-align: right; 16 | color:#FFF; 17 | transition: all 0.5s ease-out; 18 | font-weight: bold; 19 | } 20 | 21 | .low { 22 | background: #5DADE2; 23 | color:#000; 24 | } 25 | 26 | .normal { 27 | background: #F5B041; 28 | color:#000; 29 | } 30 | 31 | .high { 32 | background: #EC7063; 33 | } 34 | -------------------------------------------------------------------------------- /src/routes/systems/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import NotFound from '../notFound/NotFound'; 12 | import Systems from './Systems'; 13 | 14 | export const path = '/systems'; 15 | 16 | export const action = async () => { 17 | return { 18 | title: `Systems`, 19 | component: , 20 | }; 21 | }; 22 | 23 | export default { 24 | path, 25 | action 26 | } 27 | -------------------------------------------------------------------------------- /src/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /src/routes/error/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import ErrorPage from './ErrorPage'; 12 | 13 | export default { 14 | 15 | path: '/error', 16 | 17 | action({ error }) { 18 | return { 19 | title: error.name, 20 | description: error.message, 21 | component: , 22 | status: error.status || 500, 23 | }; 24 | }, 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /src/routes/stats-board/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import NotFound from '../notFound/NotFound'; 12 | import StatsBoard from './StatsBoard'; 13 | 14 | export const path = '/stats'; 15 | 16 | export const action = async ({ params }) => { 17 | return { 18 | title: `Stats`, 19 | component: , 20 | }; 21 | }; 22 | 23 | export default { 24 | path, 25 | action 26 | } 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | * text=auto 4 | 5 | # For the following file types, normalize line endings to LF on 6 | # checkin and prevent conversion to CRLF when they are checked out 7 | # (this is required in order to prevent newline related issues like, 8 | # for example, after the build script is run) 9 | .* text eol=lf 10 | *.html text eol=lf 11 | *.css text eol=lf 12 | *.less text eol=lf 13 | *.scss text eol=lf 14 | *.sss text eol=lf 15 | *.js text eol=lf 16 | *.json text eol=lf 17 | *.md text eol=lf 18 | *.sh text eol=lf 19 | *.txt text eol=lf 20 | *.xml text eol=lf 21 | -------------------------------------------------------------------------------- /tools/lib/fs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import fs from 'fs'; 11 | import mkdirp from 'mkdirp'; 12 | 13 | const writeFile = (file, contents) => new Promise((resolve, reject) => { 14 | fs.writeFile(file, contents, 'utf8', err => (err ? reject(err) : resolve())); 15 | }); 16 | 17 | const makeDir = (name) => new Promise((resolve, reject) => { 18 | mkdirp(name, err => (err ? reject(err) : resolve())); 19 | }); 20 | 21 | export default { writeFile, makeDir }; 22 | -------------------------------------------------------------------------------- /src/workers/Check.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | export default function (app, env, io) { 4 | 5 | app.get('/api/check', (req, res) => { 6 | const retroPieConfig = require(`../config/retropie${env}.json`); 7 | const systems = require(`../config/systems.json`); 8 | const check = {}; 9 | 10 | let count = Object.keys(systems).length; 11 | systems.map((system) => { 12 | --count; 13 | const path = `${retroPieConfig.path}/${system.path}/`; 14 | try { 15 | fs.accessSync(path, fs.F_OK); 16 | check[system.name] = true; 17 | } catch (e) { 18 | check[system.name] = false; 19 | } 20 | 21 | if (count == 0) { 22 | res.send(check); 23 | } 24 | }); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /src/routes/system/system.css: -------------------------------------------------------------------------------- 1 | @import '../../components/variables.css'; 2 | 3 | .root { 4 | margin: 0 auto; 5 | max-width: var(--max-content-width); 6 | } 7 | 8 | .container { 9 | margin: 0 auto; 10 | } 11 | 12 | .column { 13 | vertical-align: top; 14 | } 15 | 16 | .heading { 17 | margin: 25px 15px; 18 | } 19 | 20 | .heading h4 { 21 | font-size: 22px; 22 | } 23 | 24 | .fileAction { 25 | position: absolute; 26 | top: 20%; 27 | right: 10px; 28 | } 29 | 30 | .filterInput { 31 | padding: 5px; 32 | width: 100%; 33 | font-weight: normal; 34 | -webkit-font-smoothing: antialiased; 35 | } 36 | 37 | .tableHeader th { 38 | border: none; 39 | background-color: #DDDDDD; 40 | } 41 | 42 | .invalidFile td { 43 | background-color: #FF6666; 44 | } 45 | -------------------------------------------------------------------------------- /src/routes/systems/systems.css: -------------------------------------------------------------------------------- 1 | @import '../../components/variables.css'; 2 | 3 | .root { 4 | margin: 0 auto; 5 | max-width: var(--max-content-width); 6 | } 7 | 8 | .container { 9 | margin: 0 auto; 10 | } 11 | 12 | .column { 13 | vertical-align: top; 14 | } 15 | 16 | .heading { 17 | margin: 25px 15px; 18 | } 19 | 20 | .heading h4 { 21 | font-size: 22px; 22 | } 23 | 24 | .fileAction { 25 | position: absolute; 26 | top: 20%; 27 | right: 10px; 28 | } 29 | 30 | .filterInput { 31 | padding: 5px; 32 | width: 100%; 33 | font-weight: normal; 34 | -webkit-font-smoothing: antialiased; 35 | } 36 | 37 | .tableHeader th { 38 | border: none; 39 | background-color: #DDDDDD; 40 | } 41 | 42 | .invalidFile td { 43 | background-color: #FF6666; 44 | } 45 | -------------------------------------------------------------------------------- /src/actions/upload.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | 3 | import { UPLOAD } from '../constants'; 4 | 5 | export function upload(config, filesData) { 6 | const headers = new Headers(); 7 | 8 | const formData = new FormData(); 9 | 10 | formData.append('system', JSON.stringify(config)); 11 | 12 | filesData.forEach((file, i)=> { 13 | formData.append(`files[${i}]`, file, file.name); 14 | }); 15 | 16 | const payload = { 17 | method: 'POST', 18 | mode: 'cors', 19 | body: formData, 20 | headers: headers 21 | }; 22 | 23 | return { 24 | type: UPLOAD, 25 | payload: new Promise(resolve => { 26 | fetch(`/api/upload`, payload).then((response) => { 27 | resolve(response.json()); 28 | }); 29 | }) 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* Configure Mocha test runner, see package.json/scripts/test */ 11 | 12 | process.env.NODE_ENV = 'test'; 13 | 14 | function noop() { 15 | return null; 16 | } 17 | 18 | require.extensions['.css'] = noop; 19 | require.extensions['.scss'] = noop; 20 | require.extensions['.md'] = noop; 21 | require.extensions['.png'] = noop; 22 | require.extensions['.svg'] = noop; 23 | require.extensions['.jpg'] = noop; 24 | require.extensions['.jpeg'] = noop; 25 | require.extensions['.gif'] = noop; 26 | -------------------------------------------------------------------------------- /tools/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import webpack from 'webpack'; 11 | import webpackConfig from './webpack.config'; 12 | 13 | /** 14 | * Creates application bundles from the source files. 15 | */ 16 | function bundle() { 17 | return new Promise((resolve, reject) => { 18 | webpack(webpackConfig).run((err, stats) => { 19 | if (err) { 20 | return reject(err); 21 | } 22 | 23 | console.log(stats.toString(webpackConfig[0].stats)); 24 | return resolve(); 25 | }); 26 | }); 27 | } 28 | 29 | export default bundle; 30 | -------------------------------------------------------------------------------- /tools/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import run from './run'; 11 | import clean from './clean'; 12 | import copy from './copy'; 13 | import bundle from './bundle'; 14 | import render from './render'; 15 | 16 | /** 17 | * Compiles the project from source files into a distributable 18 | * format and copies it to the output (build) folder. 19 | */ 20 | async function build() { 21 | await run(clean); 22 | await run(copy); 23 | await run(bundle); 24 | 25 | if (process.argv.includes('--static')) { 26 | await run(render); 27 | } 28 | } 29 | 30 | export default build; 31 | -------------------------------------------------------------------------------- /src/actions/list.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | 3 | import { FETCH_LIST, DELETE_FILE } from '../constants'; 4 | 5 | export function list(system) { 6 | return { 7 | type: FETCH_LIST, 8 | payload: new Promise(resolve => { 9 | fetch(`/api/list/${system}`).then(response => { 10 | resolve(response.json()); 11 | }); 12 | }) 13 | }; 14 | } 15 | 16 | export function deleteFile(system, file) { 17 | const formData = new FormData(); 18 | 19 | formData.append('file', file); 20 | 21 | const payload = { 22 | method: 'POST', 23 | body: formData, 24 | }; 25 | 26 | return { 27 | type: DELETE_FILE, 28 | payload: new Promise(resolve => { 29 | fetch(`/api/deletefile/${system}`, payload).then(response => { 30 | resolve(response.json()); 31 | }); 32 | }) 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/actions/check.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | import fileExtension from 'file-extension'; 3 | 4 | import { CHECK_FOLDERS, CHECK_INVALID_FILES } from '../constants'; 5 | 6 | export function check() { 7 | return { 8 | type: CHECK_FOLDERS, 9 | payload: new Promise(resolve => { 10 | fetch(`/api/check`).then(response => { 11 | resolve(response.json()); 12 | }); 13 | }) 14 | }; 15 | } 16 | 17 | export function checkInvalidFiles(files, extensions) { 18 | return { 19 | type: CHECK_INVALID_FILES, 20 | payload: new Promise(resolve => { 21 | let invalid = []; 22 | files.map(filename => { 23 | const ext = fileExtension(filename); 24 | if (extensions.indexOf(`.${ext}`) == -1) { 25 | invalid.push(filename); 26 | } 27 | }); 28 | 29 | resolve(invalid); 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { PropTypes } from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import s from './Layout.css'; 13 | import Header from '../Header'; 14 | import Footer from '../Footer'; 15 | 16 | function Layout({ children }) { 17 | return ( 18 |
19 |
20 | {React.Children.only(children)} 21 |
22 |
23 | ); 24 | } 25 | 26 | Layout.propTypes = { 27 | children: PropTypes.element.isRequired, 28 | }; 29 | 30 | export default withStyles(s)(Layout); 31 | -------------------------------------------------------------------------------- /src/workers/Stats.js: -------------------------------------------------------------------------------- 1 | import measureCPUUsage from '../helpers/cpu'; 2 | import memory from '../helpers/memory'; 3 | 4 | const interval = 5000; // 5 sec 5 | 6 | function calculate(socket) { 7 | const disk = require('diskusage'); 8 | 9 | disk.check('/', (err, info) => { 10 | const availablePercentage = ((info.available / info.total) * 100).toFixed(2); 11 | const disk = { 12 | availablePercentage, 13 | ...info 14 | }; 15 | 16 | const mem = memory(); 17 | 18 | measureCPUUsage().then(cpu => { 19 | socket.emit('system_health', { disk, cpu, memory: mem }); 20 | }); 21 | }); 22 | } 23 | 24 | export default function (app, env, io) 25 | { 26 | io.sockets.on('connection', (socket) => { 27 | // First call 28 | calculate(socket); 29 | 30 | // Update every x seconds 31 | setInterval(calculate.bind(this, socket), interval); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /src/store/createHelpers.js: -------------------------------------------------------------------------------- 1 | import fetch from '../core/fetch'; 2 | 3 | function createFetchKnowingCookie({ cookie }) { 4 | if (!process.env.BROWSER) { 5 | return (url, options = {}) => { 6 | const isLocalUrl = /^\/($|[^\/])/.test(url); 7 | 8 | // pass cookie only for itself. 9 | // We can't know cookies for other sites BTW 10 | if (isLocalUrl && options.credentials === 'include') { 11 | const headers = { 12 | ...options.headers, 13 | cookie, 14 | }; 15 | return fetch(url, { ...options, headers }); 16 | } 17 | 18 | return fetch(url, options); 19 | }; 20 | } 21 | 22 | return fetch; 23 | } 24 | 25 | export default function createHelpers(config) { 26 | const fetchKnowingCookie = createFetchKnowingCookie(config); 27 | 28 | return { 29 | fetch: fetchKnowingCookie, 30 | history: config.history, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/core/fetch/fetch.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import Promise from 'bluebird'; 11 | import fetch, { Request, Headers, Response } from 'node-fetch'; 12 | import { host } from '../../config'; 13 | 14 | fetch.Promise = Promise; 15 | Response.Promise = Promise; 16 | 17 | function localUrl(url) { 18 | if (url.startsWith('//')) { 19 | return `https:${url}`; 20 | } 21 | 22 | if (url.startsWith('http')) { 23 | return url; 24 | } 25 | 26 | return `http://${host}${url}`; 27 | } 28 | 29 | function localFetch(url, options) { 30 | return fetch(localUrl(url), options); 31 | } 32 | 33 | export { localFetch as default, Request, Headers, Response }; 34 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-env mocha */ 11 | /* eslint-disable padded-blocks, no-unused-expressions */ 12 | 13 | import React from 'react'; 14 | import { expect } from 'chai'; 15 | import { shallow } from 'enzyme'; 16 | import App from '../App'; 17 | import Layout from './Layout'; 18 | 19 | describe('Layout', () => { 20 | 21 | it('renders children correctly', () => { 22 | const wrapper = shallow( 23 | {} }}> 24 | 25 |
26 | 27 | 28 | ); 29 | 30 | expect(wrapper.contains(
)).to.be.true; 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /src/routes/system/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import NotFound from '../notFound/NotFound'; 12 | import System from './System'; 13 | import { findSystemById } from '../../helpers'; 14 | 15 | export const path = '/system/:id'; 16 | 17 | export const action = async ({ params }) => { 18 | const system = findSystemById(params.id); 19 | if (!system) { 20 | return { 21 | title: 'Unknown', 22 | component: (), 23 | }; 24 | } 25 | 26 | return { 27 | title: system.title, 28 | component: (), 29 | }; 30 | }; 31 | 32 | export default { 33 | path, 34 | action 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Upload/Upload.css: -------------------------------------------------------------------------------- 1 | .inline { 2 | display: inline-block; 3 | width: 49%; 4 | vertical-align: top; 5 | } 6 | 7 | .centered { 8 | text-align: center; 9 | margin: 0 auto; 10 | } 11 | 12 | .container { 13 | 14 | } 15 | 16 | .form { 17 | 18 | } 19 | 20 | .fileInfo { 21 | float: right; 22 | clear: both; 23 | } 24 | 25 | .dropzone { 26 | border: 1px dashed #000; 27 | padding: 80px 10px; 28 | font-size: 18px; 29 | text-align: center; 30 | border-radius: 5px 5px 5px 5px; 31 | -webkit-transition: background .5s; 32 | -moz-transition: background .5s; 33 | -ms-transition: background .5s; 34 | -o-transition: background .5s; 35 | transition: background .5s; 36 | } 37 | 38 | .disabled { 39 | opacity: .5; 40 | } 41 | 42 | /** Sadly dropzone doest play very well with unknown file types, so active and reject are the same**/ 43 | .dropzoneActive { 44 | background-color: #b3d4fc; 45 | } 46 | 47 | .dropzoneReject { 48 | background-color: #b3d4fc; 49 | } 50 | -------------------------------------------------------------------------------- /src/reducers/upload.js: -------------------------------------------------------------------------------- 1 | import {Record} from 'immutable'; 2 | import * as actions from '../constants'; 3 | 4 | const InitialState = Record({ 5 | isUploading: false, 6 | error: null, 7 | }); 8 | 9 | const initialState = new InitialState; 10 | 11 | export default function runtime(state = initialState, action) { 12 | if (!(state instanceof InitialState)) return initialState; 13 | 14 | switch (action.type) { 15 | case actions.UPLOAD_LOADING: 16 | { 17 | return state.set('isUploading', true) 18 | .set('error', null); 19 | } 20 | 21 | case actions.UPLOAD_SUCCESS: 22 | { 23 | return state.set('isUploading', false) 24 | .set('error', action.payload.error || null); 25 | } 26 | 27 | case actions.UPLOAD_ERROR: 28 | { 29 | return state.set('isUploading', false) 30 | .set('error', action.payload.error || 'Server error'); 31 | } 32 | 33 | default: 34 | return state; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/routes/notFound/NotFound.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { PropTypes } from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import Layout from '../../components/Layout'; 13 | import s from './NotFound.css'; 14 | 15 | function NotFound({ title }) { 16 | return ( 17 | 18 |
19 |
20 |

{title}

21 |

Sorry, the page you were trying to view does not exist.

22 |
23 |
24 |
25 | ); 26 | } 27 | 28 | NotFound.propTypes = { 29 | title: PropTypes.string.isRequired, 30 | }; 31 | 32 | export default withStyles(s)(NotFound); 33 | -------------------------------------------------------------------------------- /src/components/Navigation/Navigation.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | .root { 11 | height: 70px; 12 | margin: 0 auto; 13 | } 14 | 15 | .link { 16 | display: inline-block; 17 | padding: 8px; 18 | text-decoration: none; 19 | font-size: 1.125em; /* ~18px */ 20 | color: #FFF; 21 | font-weight: bold; 22 | transition: all .5s ease-out; 23 | } 24 | 25 | .link, 26 | .link:active, 27 | .link:visited { 28 | padding: 20px 20px; 29 | text-decoration: none; 30 | } 31 | 32 | .link:hover { 33 | color: rgba(255, 255, 255, 1); 34 | background-color: #f3610b; 35 | text-decoration: none; 36 | } 37 | 38 | .active, .active:hover { 39 | background-color: #333333; 40 | } 41 | 42 | .spacer { 43 | color: rgba(255, 255, 255, 0.3); 44 | } 45 | -------------------------------------------------------------------------------- /tools/retropie-web-gui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: retropie-web 5 | # Required-Start: $local_fs 6 | # Required-Stop: $local_fs 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: retropie-web 10 | ### END INIT INFO 11 | 12 | DAEMON_PATH="/home/pi/web/retropie-web-gui" 13 | DAEMON=pm2 14 | NAME="retropie-web" 15 | 16 | case "$1" in 17 | start) 18 | printf "%-50s" "Starting $NAME...\n" 19 | sudo pm2 start $DAEMON_PATH/server.js -s -f --name="$NAME" -l /var/logs/$NAME.log -- --release 20 | ;; 21 | 22 | status) 23 | printf "%-50s" "Checking $NAME...\n" 24 | sudo pm2 status $NAME 25 | ;; 26 | 27 | stop) 28 | printf "%-50s" "Stopping $NAME\n" 29 | sudo pm2 stop -s $NAME 30 | ;; 31 | 32 | restart) 33 | printf "%-50s" "Restarting $NAME\n" 34 | sudo pm2 restart -m $NAME 35 | ;; 36 | 37 | *) 38 | echo "Usage: $0 {status|start|stop|restart}\n" 39 | exit 1 40 | esac 41 | -------------------------------------------------------------------------------- /src/workers/Temp.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import sys from 'util'; 3 | import { exec } from 'child_process'; 4 | 5 | const interval = 1000; 6 | 7 | let child; 8 | 9 | function calculate(cmd, socket) { 10 | child = exec(cmd, (error, stdout, stderr) => { 11 | if (error !== null) { 12 | socket.emit('temperatureUpdate', { 13 | error: Object.assign({}, error, { message: stderr }) 14 | }); 15 | } else { 16 | const temp = parseFloat(stdout) / 1000; 17 | socket.emit('temperatureUpdate', { temp }); 18 | } 19 | }); 20 | } 21 | 22 | export default function (app, env, io) { 23 | 24 | const retroPieConfig = require(`../config/retropie${env}.json`); 25 | 26 | // Once Socket is connected, we start publishing the temp 27 | io.sockets.on('connection', (socket) => { 28 | // First call 29 | calculate(retroPieConfig.temp_cmd, socket); 30 | 31 | // Update every x seconds 32 | setInterval(calculate.bind(this, retroPieConfig.temp_cmd, socket), interval); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import s from './Footer.css'; 13 | import Link from '../Link'; 14 | 15 | function Footer() { 16 | return ( 17 |
18 |
19 | © 20 | 21 | Retropie 22 | - 23 | 24 | 25 | 26 |
27 |
28 | ); 29 | } 30 | 31 | export default withStyles(s)(Footer); 32 | -------------------------------------------------------------------------------- /src/routes/error/ErrorPage.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | * { 11 | line-height: 1.2; 12 | margin: 0; 13 | } 14 | 15 | html { 16 | color: #888; 17 | display: table; 18 | font-family: sans-serif; 19 | height: 100%; 20 | text-align: center; 21 | width: 100%; 22 | } 23 | 24 | body { 25 | display: table-cell; 26 | vertical-align: middle; 27 | padding: 2em; 28 | } 29 | 30 | h1 { 31 | color: #555; 32 | font-size: 2em; 33 | font-weight: 400; 34 | } 35 | 36 | p { 37 | margin: 0 auto; 38 | width: 280px; 39 | } 40 | 41 | pre { 42 | text-align: left; 43 | margin-top: 2rem; 44 | } 45 | 46 | @media only screen and (max-width: 280px) { 47 | body, 48 | p { 49 | width: 95%; 50 | } 51 | 52 | h1 { 53 | font-size: 1.5em; 54 | margin: 0 0 0.3em; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/routes/home/Home.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | width: var(--max-content-width); 14 | margin: 0 auto; 15 | padding-left: 20px; 16 | padding-right: 20px; 17 | } 18 | 19 | .container { 20 | margin: 0 auto; 21 | padding: 0 0 40px; 22 | } 23 | 24 | .bigButtons { 25 | list-style: none; 26 | margin: 0; 27 | } 28 | 29 | .bigButtons li { 30 | display: inline-block; 31 | border: 2px solid var(--link-color); 32 | width: 180px; 33 | padding: 5px 10px 0; 34 | font-size: 100px; 35 | text-align: center; 36 | margin: 10px; 37 | } 38 | 39 | .bigButtons li a { 40 | vertical-align: top; 41 | } 42 | 43 | .bigButtons li a p { 44 | font-size: 14px; 45 | } 46 | 47 | .bigButtons li a:hover { 48 | text-decoration: none; 49 | } 50 | -------------------------------------------------------------------------------- /src/routes/error/ErrorPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { PropTypes } from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import s from './ErrorPage.css'; 13 | 14 | function ErrorPage({ error }) { 15 | if (process.env.NODE_ENV !== 'production') { 16 | return ( 17 |
18 |

{error.name}

19 |

{error.message}

20 |
{error.stack}
21 |
22 | ); 23 | } 24 | 25 | return ( 26 |
27 |

Error

28 |

Sorry, a critical error occurred on this page.

29 |
30 | ); 31 | } 32 | 33 | ErrorPage.propTypes = { 34 | error: PropTypes.object.isRequired, 35 | }; 36 | 37 | export { ErrorPage as ErrorPageWithoutStyle }; 38 | export default withStyles(s)(ErrorPage); 39 | -------------------------------------------------------------------------------- /src/components/variables.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | :root { 11 | /* 12 | * Typography 13 | * ======================================================================== */ 14 | 15 | --font-family-base: 'Segoe UI', 'HelveticaNeue-Light', sans-serif; 16 | 17 | /* 18 | * Layout 19 | * ======================================================================== */ 20 | 21 | --max-content-width: 1170px; 22 | 23 | /* 24 | * Media queries breakpoints 25 | * ======================================================================== */ 26 | 27 | --screen-xs-min: 480px; /* Extra small screen / phone */ 28 | --screen-sm-min: 768px; /* Small screen / tablet */ 29 | --screen-md-min: 992px; /* Medium screen / desktop */ 30 | --screen-lg-min: 1200px; /* Large screen / wide desktop */ 31 | 32 | --link-color: #0074c2; 33 | } 34 | -------------------------------------------------------------------------------- /src/workers/Upload.js: -------------------------------------------------------------------------------- 1 | import fileUpload from 'express-fileupload'; 2 | import cors from 'cors'; 3 | import _ from 'lodash'; 4 | 5 | export default function (app, env, io) { 6 | const retroPieConfig = require(`../config/retropie${env}.json`); 7 | 8 | app.use(fileUpload()); 9 | 10 | app.post('/api/upload', cors(), (req, res, next) => { 11 | if (!req.files) { 12 | res.send({ result: false, error: 'No files were uploaded.' }); 13 | return; 14 | } 15 | 16 | const system = JSON.parse(req.body.system); 17 | const files = req.files; 18 | 19 | let remaining = Object.keys(files).length; 20 | _.forEach(files, (file) => { 21 | file.mv(`${retroPieConfig.path}/${system.path}/${file.name}`, (err) => { 22 | --remaining; 23 | if (remaining === 0 || err) { 24 | const result = { 25 | result: err === null, 26 | error: err ? err.message : null, 27 | }; 28 | 29 | if (err) { 30 | res.status(500); 31 | } 32 | 33 | res.send(result); 34 | } 35 | }); 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /src/reducers/check.js: -------------------------------------------------------------------------------- 1 | import {Record} from 'immutable'; 2 | import * as actions from '../constants'; 3 | 4 | const InitialState = Record({ 5 | isChecking: true, 6 | isCheckingInvalids: false, 7 | invalidFiles: [], 8 | list: [], 9 | }); 10 | 11 | const initialState = new InitialState; 12 | 13 | export default function check(state = initialState, action) { 14 | if (typeof state == undefined) { 15 | return initialState; 16 | } 17 | 18 | switch (action.type) { 19 | case actions.CHECK_FOLDERS_LOADING: 20 | { 21 | return state.set('isChecking', true); 22 | } 23 | 24 | case actions.CHECK_FOLDERS_SUCCESS: 25 | { 26 | return state.set('isChecking', false) 27 | .set('list', action.payload); 28 | } 29 | 30 | case actions.CHECK_INVALID_FILES_LOADING: { 31 | return state.set('invalidFiles', []) 32 | .set('isCheckingInvalids', true); 33 | } 34 | 35 | case actions.CHECK_INVALID_FILES_SUCCESS: { 36 | return state.set('invalidFiles', action.payload) 37 | .set('isCheckingInvalids', false); 38 | } 39 | } 40 | 41 | return state; 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Fernando Giovanini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import classnames from 'classnames'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | 14 | import Navigation from '../Navigation'; 15 | import MiniStats from '../MiniStats'; 16 | import logo from './RetroPie_Logo-60x60.png'; 17 | import s from './Header.css'; 18 | 19 | function Header() { 20 | return ( 21 |
22 |
23 |
24 | 25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 |
33 | ); 34 | } 35 | 36 | export default withStyles(s)(Header); 37 | -------------------------------------------------------------------------------- /ReactStarterKit.LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2016 Konstantin Tarkus, KriaSoft LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | -------------------------------------------------------------------------------- /src/workers/Delete.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import cors from 'cors'; 3 | 4 | export default function (app, env, io) { 5 | const retroPieConfig = require(`../config/retropie${env}.json`); 6 | const systems = require(`../config/systems.json`); 7 | 8 | app.post('/api/deletefile/:system', cors(), (req, res) => { 9 | let result = false; 10 | let error = null; 11 | 12 | const { system } = req.params; 13 | const index = _.findIndex(systems, (plt) => { 14 | return plt.name == system; 15 | }); 16 | 17 | if (index > -1) { 18 | if (req.body.file) { 19 | const fileName = req.body.file; 20 | const systemConfig = systems[index]; 21 | const path = `${retroPieConfig.path}/${systemConfig.path}/${fileName}`; 22 | 23 | try { 24 | fs.unlinkSync(path); 25 | result = true; 26 | } catch (e) { 27 | error = e.message; 28 | } 29 | } else { 30 | error = 'No file sent'; 31 | } 32 | } else { 33 | error = 'Unknown system name'; 34 | } 35 | 36 | if (error) { 37 | res.status(400); 38 | } 39 | 40 | res.send({ result, error }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /tools/copy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import path from 'path'; 11 | import gaze from 'gaze'; 12 | import Promise from 'bluebird'; 13 | import fs from './lib/fs'; 14 | import pkg from '../package.json'; 15 | /** 16 | * Copies static files such as robots.txt, favicon.ico to the 17 | * output (build) folder. 18 | */ 19 | async function copy({ watch } = {}) { 20 | const ncp = Promise.promisify(require('ncp')); 21 | 22 | await Promise.all([ 23 | ncp('src/public', 'build/public'), 24 | ncp('src/config', 'build/config'), 25 | ncp('src/images', 'build/public/images'), 26 | ncp('yarn.lock', 'build/yarn.lock'), 27 | ]); 28 | 29 | await fs.writeFile('./build/package.json', JSON.stringify({ 30 | private: true, 31 | engines: pkg.engines, 32 | dependencies: pkg.dependencies, 33 | scripts: { 34 | start: 'node server.js', 35 | }, 36 | }, null, 2)); 37 | } 38 | 39 | export default copy; 40 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../variables.css'; 11 | 12 | .root { 13 | background: #333; 14 | color: #fff; 15 | position: fixed; 16 | bottom: 0; 17 | width: 100%; 18 | height: 40px; 19 | } 20 | 21 | .container { 22 | margin: 0 auto; 23 | padding: 10px 15px; 24 | max-width: var(--max-content-width); 25 | text-align: center; 26 | } 27 | 28 | .text { 29 | color: rgba(255, 255, 255, 0.5); 30 | } 31 | 32 | .textMuted { 33 | composes: text; 34 | color: rgba(255, 255, 255, 0.3); 35 | } 36 | 37 | .spacer { 38 | color: rgba(255, 255, 255, 0.3); 39 | } 40 | 41 | .text, 42 | .link { 43 | padding: 2px 5px; 44 | font-size: 1em; 45 | } 46 | 47 | .link, 48 | .link:active, 49 | .link:visited { 50 | color: rgba(255, 255, 255, 0.6); 51 | text-decoration: none; 52 | } 53 | 54 | .link:hover { 55 | color: rgba(255, 255, 255, 1); 56 | } 57 | 58 | .root a { 59 | color:#FFF; 60 | } 61 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-disable global-require */ 11 | 12 | // The top-level (parent) route 13 | export default { 14 | 15 | path: '/', 16 | 17 | // Keep in mind, routes are evaluated in order 18 | children: [ 19 | require('./home').default, 20 | require('./systems').default, 21 | require('./system').default, 22 | require('./stats-board').default, 23 | require('./notFound').default, 24 | ], 25 | 26 | async action({ next }) { 27 | let route; 28 | 29 | // Execute each child route until one of them return the result 30 | // TODO: move this logic to the `next` function 31 | do { 32 | route = await next(); 33 | } while (!route); 34 | 35 | // Provide default values for title, description etc. 36 | route.title = `RetroPie - ${route.title || 'Untitled Page'}`; 37 | route.description = route.description || ''; 38 | 39 | return route; 40 | }, 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/ProgressBar/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import classnames from 'classnames'; 3 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 4 | 5 | import s from './progressbar.css'; 6 | 7 | class ProgressBar extends Component 8 | { 9 | render() { 10 | const { percentage, max, symbol, low, normal } = this.props; 11 | 12 | let levelClassName = s.low; 13 | if (percentage >= low && percentage <= normal) { 14 | levelClassName = s.normal; 15 | } else if (percentage > normal) { 16 | levelClassName = s.high; 17 | } 18 | 19 | const sizePercentage = percentage * 100 / max; 20 | 21 | return ( 22 |
23 |
{percentage}
24 |
25 | ); 26 | } 27 | } 28 | 29 | ProgressBar.propTypes = { 30 | max: PropTypes.number, 31 | symbol: PropTypes.string, 32 | }; 33 | 34 | ProgressBar.defaultProps = { 35 | min:0, 36 | max: 100, 37 | low: 33, 38 | normal: 70, 39 | symbol: '%' 40 | }; 41 | 42 | export default withStyles(s)(ProgressBar); 43 | -------------------------------------------------------------------------------- /src/components/SystemHealth.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux' 3 | import history from '../core/history'; 4 | 5 | import io from 'socket.io-client'; 6 | 7 | import * as actions from '../actions/stats'; 8 | 9 | const mapStateToProps = (state) => { 10 | return { } 11 | }; 12 | 13 | const mapDispatchToProps = (dispatch) => { 14 | return { 15 | update: data => dispatch(actions.update(data)), 16 | updateTemp: data => dispatch(actions.updateTemp(data)), 17 | } 18 | }; 19 | 20 | const statEvent = 'system_health'; 21 | const tempEvent = 'temperatureUpdate'; 22 | 23 | class SystemHealth extends Component 24 | { 25 | socket; 26 | 27 | componentDidMount() { 28 | const { port, ip } = this.props; 29 | this.socket = io(`http://${ip}:${port}`); 30 | 31 | this.socket.on(statEvent, ::this.props.update); 32 | this.socket.on(tempEvent, ::this.props.updateTemp); 33 | } 34 | 35 | componentWillUnmount() { 36 | this.socket.removeAllListeners(statEvent); 37 | this.socket.removeAllListeners(tempEvent); 38 | } 39 | 40 | render() { 41 | return(); 42 | } 43 | } 44 | 45 | export default connect(mapStateToProps, mapDispatchToProps)(SystemHealth); 46 | -------------------------------------------------------------------------------- /src/reducers/stats.js: -------------------------------------------------------------------------------- 1 | import { Record } from 'immutable'; 2 | import * as actions from '../constants'; 3 | 4 | import Disk from '../models/Disk'; 5 | import CPU from '../models/CPU'; 6 | import Memory from '../models/Memory'; 7 | import Temp from '../models/Temp'; 8 | 9 | const InitialState = Record({ 10 | isChecking: false, 11 | disk: new Disk(), 12 | cpu: new CPU(), 13 | memory: new Memory(), 14 | temp: new Temp() 15 | }); 16 | 17 | const initialState = new InitialState; 18 | 19 | export default function reducer(state = initialState, action) { 20 | 21 | if (!(state instanceof InitialState)) return initialState; 22 | 23 | switch (action.type) { 24 | case actions.SYSTEM_STATS_LOADING: { 25 | return state.set('isChecking', true); 26 | } 27 | 28 | case actions.SYSTEM_STATS_SUCCESS: { 29 | return state.set('isChecking', false) 30 | .set('disk', new Disk(action.payload.disk)) 31 | .set('cpu', new CPU(action.payload.cpu)) 32 | .set('memory', new Memory(action.payload.memory)) 33 | } 34 | 35 | case actions.SYSTEM_TEMPERATURE_SUCCESS: { 36 | return state.set('temp', new Temp(action.payload)) 37 | } 38 | } 39 | 40 | return state; 41 | } 42 | -------------------------------------------------------------------------------- /src/workers/List.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import _ from 'lodash'; 3 | 4 | export default function (app, env, io) { 5 | 6 | app.get('/api/list/:system', (req, res) => { 7 | const { system } = req.params; 8 | 9 | const retroPieConfig = require(`../config/retropie${env}.json`); 10 | const systems = require(`../config/systems.json`); 11 | 12 | const index = _.findIndex(systems, (plt) => { 13 | return plt.name == system; 14 | }); 15 | 16 | let error = null; 17 | let available = false; 18 | let fileList = []; 19 | 20 | if (index > -1) { 21 | const systemConfig = systems[index]; 22 | const path = `${retroPieConfig.path}/${systemConfig.path}/`; 23 | try { 24 | try { 25 | fs.accessSync(path, fs.F_OK); 26 | available = true; 27 | } catch (e) { 28 | error = e.message; 29 | } 30 | 31 | if (available) { 32 | fileList = fs.readdirSync(path); 33 | } 34 | } catch (e) { 35 | error = e.message; 36 | } 37 | } else { 38 | error = 'Unknown system name'; 39 | } 40 | 41 | if (error) { 42 | res.status(400) 43 | } 44 | 45 | res.send({ system, available, error, fileList }); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /src/routes/systems/Systems.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux' 3 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 4 | import Layout from '../../components/Layout'; 5 | import Link from '../../components/Link/Link'; 6 | import SystemsList from '../../components/SystemsList'; 7 | import * as actions from '../../actions/check'; 8 | import s from './systems.css'; 9 | 10 | const mapStateToProps = (state) => { 11 | return { 12 | isChecking: state.check.get('isChecking'), 13 | checkList: state.check.get('list'), 14 | } 15 | }; 16 | 17 | const mapDispatchToProps = (dispatch) => { 18 | return { 19 | onLoad: () => dispatch(actions.check()), 20 | } 21 | }; 22 | 23 | class Systems extends Component 24 | { 25 | componentDidMount() { 26 | this.props.onLoad(); 27 | } 28 | 29 | render() { 30 | const { isChecking, checkList } = this.props; 31 | 32 | return ( 33 | 34 |
35 | 36 |
37 |
38 | ) 39 | } 40 | } 41 | 42 | export default connect( 43 | mapStateToProps, 44 | mapDispatchToProps 45 | )(withStyles(s)(Systems)); 46 | -------------------------------------------------------------------------------- /tools/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | function format(time) { 11 | return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); 12 | } 13 | 14 | function run(fn, options) { 15 | const task = typeof fn.default === 'undefined' ? fn : fn.default; 16 | const start = new Date(); 17 | console.log( 18 | `[${format(start)}] Starting '${task.name}${options ? `(${options})` : ''}'...` 19 | ); 20 | return task(options).then(resolution => { 21 | const end = new Date(); 22 | const time = end.getTime() - start.getTime(); 23 | console.log( 24 | `[${format(end)}] Finished '${task.name}${options ? `(${options})` : ''}' after ${time} ms` 25 | ); 26 | return resolution; 27 | }); 28 | } 29 | 30 | if (require.main === module && process.argv.length > 2) { 31 | delete require.cache[__filename]; // eslint-disable-line no-underscore-dangle 32 | const module = require(`./${process.argv[2]}.js`).default; // eslint-disable-line import/no-dynamic-require 33 | run(module).catch(err => { console.error(err.stack); process.exit(1); }); 34 | } 35 | 36 | export default run; 37 | -------------------------------------------------------------------------------- /src/components/SystemsList/SystemsList.css: -------------------------------------------------------------------------------- 1 | @import '../../components/variables.css'; 2 | 3 | .container { 4 | margin: 0 auto; 5 | max-width: var(--max-content-width); 6 | } 7 | 8 | .list { 9 | margin: 0; 10 | } 11 | 12 | .system { 13 | cursor: pointer; 14 | position: relative; 15 | vertical-align: top; 16 | text-align: center; 17 | list-style: none; 18 | width: 150px; 19 | height: 150px; 20 | padding: 10px; 21 | display: inline-block; 22 | margin: 5px; 23 | color: var(--link-color); 24 | border: 2px solid var(--link-color); 25 | background: #fff; 26 | -webkit-border-radius: 5px; 27 | -moz-border-radius: 5px; 28 | border-radius: 5px; 29 | transition: all .3s; 30 | } 31 | 32 | .system:hover { 33 | border-color: #f3610b; 34 | } 35 | 36 | .system a { 37 | margin: 0 auto; 38 | } 39 | 40 | .system.hidden { 41 | display: none; 42 | } 43 | 44 | .systemTitle { 45 | position: absolute; 46 | left:0; 47 | bottom: 5px; 48 | width: 100%; 49 | } 50 | 51 | .availabilityCheckIcon { 52 | position: absolute; 53 | top:-5px; 54 | right: -5px; 55 | -webkit-border-radius: 15px; 56 | -moz-border-radius: 15px; 57 | border-radius: 15px; 58 | width: 25px; 59 | height: 25px; 60 | padding:5px; 61 | color:#FFF; 62 | } 63 | 64 | .available { 65 | background-color: green; 66 | } 67 | 68 | .unavailable { 69 | background-color: red; 70 | } 71 | -------------------------------------------------------------------------------- /src/components/Header/Header.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../variables.css'; 11 | 12 | :root { 13 | --brand-color: #61dafb; 14 | } 15 | 16 | .root { 17 | background: #fff; 18 | color: #fff; 19 | } 20 | 21 | .container { 22 | padding: 0; 23 | } 24 | 25 | .innerContainer { 26 | margin: 0 auto; 27 | max-width: var(--max-content-width); 28 | } 29 | 30 | .brand { 31 | color: color(var(--brand-color) lightness(+10%)); 32 | text-decoration: none; 33 | font-size: 1.75em; /* ~28px */ 34 | display: inline-block; 35 | padding:5px 0; 36 | margin-right: 10px; 37 | } 38 | 39 | .brand span { 40 | float: right; 41 | } 42 | 43 | .brandTxt { 44 | margin-left: 10px; 45 | } 46 | 47 | .nav { 48 | background: #000; 49 | } 50 | 51 | .navigation { 52 | max-width: var(--max-content-width); 53 | } 54 | 55 | .banner { 56 | text-align: center; 57 | } 58 | 59 | .bannerTitle { 60 | margin: 0; 61 | padding: 10px; 62 | font-weight: normal; 63 | font-size: 4em; 64 | line-height: 1em; 65 | } 66 | 67 | .bannerDesc { 68 | padding: 0; 69 | color: rgba(255, 255, 255, 0.5); 70 | font-size: 1.25em; 71 | margin: 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/routes/home/Home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { Component } from 'react'; 11 | import { connect } from 'react-redux' 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import Layout from '../../components/Layout'; 14 | import Link from '../../components/Link/Link'; 15 | 16 | import s from './Home.css'; 17 | 18 | class Home extends Component 19 | { 20 | render() { 21 | return ( 22 | 23 |
24 |
25 |
    26 |
  • 27 | 28 | 29 |

    System Stats

    30 | 31 |
  • 32 |
  • 33 | 34 | 35 |

    Systems

    36 | 37 |
  • 38 |
39 |
40 |
41 |
42 | ) 43 | } 44 | } 45 | 46 | export default withStyles(s)(Home); 47 | -------------------------------------------------------------------------------- /src/components/Link/Link.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { Component, PropTypes } from 'react'; 11 | import history from '../../core/history'; 12 | 13 | function isLeftClickEvent(event) { 14 | return event.button === 0; 15 | } 16 | 17 | function isModifiedEvent(event) { 18 | return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); 19 | } 20 | 21 | class Link extends Component { 22 | 23 | static propTypes = { 24 | to: PropTypes.string.isRequired, 25 | children: PropTypes.node, 26 | onClick: PropTypes.func, 27 | }; 28 | 29 | handleClick = (event) => { 30 | if (this.props.onClick) { 31 | this.props.onClick(event); 32 | } 33 | 34 | if (isModifiedEvent(event) || !isLeftClickEvent(event)) { 35 | return; 36 | } 37 | 38 | if (event.defaultPrevented === true) { 39 | return; 40 | } 41 | 42 | event.preventDefault(); 43 | history.push(this.props.to); 44 | }; 45 | 46 | render() { 47 | const { to, children, ...props } = this.props; 48 | return {children}; 49 | } 50 | 51 | } 52 | 53 | export default Link; 54 | -------------------------------------------------------------------------------- /tools/render.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import fetch from 'node-fetch'; 11 | import runServer from './runServer'; 12 | import fs from './lib/fs'; 13 | import { host } from '../src/config'; 14 | 15 | // Enter your paths here which you want to render as static 16 | const routes = [ 17 | '/', 18 | '/404', // https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/ 19 | ]; 20 | 21 | async function render() { 22 | let server; 23 | await new Promise((resolve) => { 24 | server = runServer(resolve); 25 | }); 26 | 27 | await routes.reduce((promise, route) => promise.then(async () => { 28 | const url = `http://${host}${route}`; 29 | const dir = `build/public${route.replace(/[^\/]*$/, '')}`; 30 | const name = route.endsWith('/') ? 'index.html' : `${route.match(/[^/]+$/)[0]}.html`; 31 | const dist = `${dir}${name}`; 32 | const res = await fetch(url); 33 | const text = await res.text(); 34 | await fs.makeDir(dir); 35 | await fs.writeFile(dist, text); 36 | console.log(`${dist} => ${res.status} ${res.statusText}`); 37 | }), Promise.resolve()); 38 | 39 | server.kill('SIGTERM'); 40 | } 41 | 42 | export default render; 43 | -------------------------------------------------------------------------------- /src/components/Navigation/Navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { PropTypes } from 'react'; 11 | import cx from 'classnames'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Navigation.css'; 14 | import Link from '../Link'; 15 | import history from '../../core/history'; 16 | 17 | const locations = [ 18 | { 19 | path: '/', 20 | title: 'Home', 21 | }, 22 | { 23 | path: '/systems', 24 | title: 'Systems', 25 | }, 26 | { 27 | path: '/stats', 28 | title: 'Stats', 29 | } 30 | ]; 31 | 32 | function Navigation({ className, children }) { 33 | const active = history.location ? history.location.pathname : '/'; 34 | return ( 35 |
36 | {children} 37 | {locations.map( location => {location.title} )} 38 |
39 | ); 40 | } 41 | 42 | Navigation.propTypes = { 43 | className: PropTypes.string, 44 | children: PropTypes.node 45 | }; 46 | 47 | export default withStyles(s)(Navigation); 48 | -------------------------------------------------------------------------------- /src/components/Html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { PropTypes } from 'react'; 11 | 12 | function Html({ title, description, style, script, children }) { 13 | return ( 14 | 15 | 16 | 17 | 18 | {title} 19 | 20 | 21 | 22 | 23 | 24 | 25 | {style &&