Huge commit

- Update all deps
 - Apply midwest changes
 - Convert all templates to JSX
 - Preact instead of Marko
 - Babel & Eslint
This commit is contained in:
Linus Miller 2016-11-10 14:17:22 +01:00
parent ddc1d5bf5a
commit 47ce0a2a33
44 changed files with 612 additions and 599 deletions

View File

@ -1,15 +1,31 @@
{
"env": {
"server": {
"node": {
"presets": [ "es2015-node6" ],
"plugins": [
"add-module-exports"
]
},
"client": {
"presets": [ "es2015", "react" ],
"node-jsx": {
"presets": [ "es2015-node6" ],
"plugins": [
"add-module-exports"
"add-module-exports",
["transform-react-jsx", { "pragma": "h" }],
["jsx-node/babel/alias", {
"lowline": "lodash",
"preact": "jsx-node"
}],
["jsx-node/babel/stringify"]
]
},
"rollup": {
"include": [
"node_modules/preact/**",
"client/**"
],
"presets": [ "es2015-rollup" ],
"plugins": [
["transform-react-jsx", { "pragma":"h" }]
]
}
}

View File

@ -1,52 +1,14 @@
{
"extends": "airbnb-base",
"parserOptions": {
"sourceType": "strict"
},
"env": {
"mocha": true
},
"rules": {
"semi": [ 2, "never" ],
"import/no-extraneous-dependencies": ["error", {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false}],
"new-cap": 0,
"no-mixed-operators": 0,
"no-cond-assign": 0,
"guard-for-in": 0,
"global-require": 0,
"no-underscore-dangle": 0,
"object-shorthand": 0,
"default-case": 0,
"one-var": 0,
"prefer-rest-params": 0,
"no-unused-vars": [ 2, { "args": "none" } ],
"no-alert": 0,
"quote-props": 0,
"no-nested-ternary": 0,
"no-use-before-define": [2, { "functions": false, "classes": true }],
"consistent-return": 0,
"no-eval": 0,
"prefer-arrow-callback": 0,
"array-bracket-spacing": 0,
"no-console": 0,
"indent": [ 2, 2, { "SwitchCase": 1 }],
"max-len": 0,
"comma-dangle": 0,
"no-param-reassign": 0,
"prefer-template": 0,
"curly": 0,
"arrow-parens": [2, "always"],
"class-methods-use-this": 0,
"func-names": 0,
"import/no-unresolved": [2, { "ignore": ["^[^./]"] }],
"no-console": [1, { "allow": ["warn", "error", "info"] }],
"no-empty": [2, { "allowEmptyCatch": true }],
"no-mixed-operators": 0,
"no-param-reassign": 0,
"no-shadow": 0,
"spaced-comment": 0,
"strict": [ 2, "global" ]
},
"globals": {
"PWD": true,
"ENV": true,
"location": true
"no-underscore-dangle": 0
}
}

4
.gitignore vendored
View File

@ -31,7 +31,9 @@ npm-debug.log
# Bower dependency directory
bower_components
*.marko.js
.tern-port
/build
/dump

View File

@ -1,37 +1,14 @@
{
"extends": "airbnb",
"extends": [
"airbnb",
"../.eslintrc"
],
"rules": {
"semi": [ 2, "never" ],
"no-confusing-arrow": 0,
"react/react-in-jsx-scope": 0,
"react/prop-types": 0,
"guard-for-in": 0,
"global-require": 0,
"no-underscore-dangle": 0,
"object-shorthand": 0,
"default-case": 0,
"one-var": 0,
"prefer-rest-params": 0,
"no-unused-vars": [ 2, { "args": "none" } ],
"no-alert": 0,
"quote-props": 0,
"no-nested-ternary": 0,
"no-use-before-define": [2, { "functions": false, "classes": true }],
"consistent-return": 0,
"no-eval": 0,
"prefer-arrow-callback": 0,
"array-bracket-spacing": 0,
"no-console": 0,
"indent": [ 2, 2, { "SwitchCase": 1 }],
"max-len": 0,
"comma-dangle": 0,
"no-param-reassign": 0,
"prefer-template": 0,
"curly": 0,
"func-names": 0,
"no-shadow": 0,
"spaced-comment": 0,
"strict": [ 2, "global" ]
"react/no-unknown-property": 0,
"jsx-a11y/label-has-for": 0
},
"env": {
@ -39,11 +16,10 @@
},
"globals": {
"ENV": true,
"google": true,
"INITIAL_STATE": true,
"INITIAL_CONTEXT": true,
"PWD": true,
"$": true
"ga": true,
"$": true,
"_": true,
"Backbone": true,
"INITIAL_CONTEXT": true
}
}

View File

@ -1,20 +0,0 @@
import { $ } from 'dollr'
import './process'
import Timer from './components/Timer'
$(() => {
// request permission on page load
if (!Notification) {
alert('Desktop notifications not available in your browser. Try Chromium, Chrome or Firefox.')
return
}
if (Notification.permission !== 'granted')
Notification.requestPermission()
const timer = $('.timer')
Timer.render().replace(timer).getWidget()
})

15
client/app.jsx Normal file
View File

@ -0,0 +1,15 @@
import { h, render } from 'preact';
import Layout from './components/Layout.jsx';
// request permission on page load
if (!Notification) {
alert('Desktop notifications not available in your browser. Try Chromium, Chrome or Firefox.');
}
if (Notification.permission !== 'granted') {
Notification.requestPermission();
}
render((
<Layout />
), null, document.getElementById('app'));

View File

@ -0,0 +1,12 @@
import { h } from 'preact';
import Timer from './Timer.jsx';
export default () => (
<div id="app">
<main>
<h1>Pomodoro Time!</h1>
<Timer />
</main>
</div>
);

109
client/components/Timer.jsx Normal file
View File

@ -0,0 +1,109 @@
import { h, Component } from 'preact';
import { bindAll } from 'lowline';
import timeFilter from '../util/time-filter';
const length = 25 * 60 * 1000;
export default class Timer extends Component {
constructor() {
super();
this.state = {
time: length,
totalTime: length,
startTime: undefined,
};
bindAll(this, ['start', 'pause', 'reset', 'updateTimer', 'end']);
}
componentDidMount() {
this.reset();
}
start() {
this.setState({
startTime: Date.now(),
});
this.interval = setInterval(this.updateTimer.bind(this), 10);
}
pause() {
clearInterval(this.interval);
}
reset() {
clearInterval(this.interval);
this.setState({
time: length,
totalTime: length,
startTime: undefined,
});
}
updateTimer() {
let time = this.state.totalTime - (Date.now() - this.state.startTime);
if (time <= 0) {
this.pause();
this.end();
time = 0;
}
document.title = timeFilter(time).slice(0, 5);
this.setState({
time,
});
}
end() {
const data = {
startTime: this.state.startTime,
endTime: new Date(),
};
document.title = 'Pling!';
if (Notification.permission !== 'granted') {
Notification.requestPermission();
} else {
const notification = new Notification('Pomodoro complete!', {
icon: 'https://www.planetnatural.com/wp-content/uploads/2014/03/tomato-supplies.jpg',
body: 'Time for a break!',
});
}
fetch('/api/pomodoros', {
method: 'POST',
body: JSON.stringify(data),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((json) => {
console.log('succes');
});
}
render(props, state) {
return (
<div class="timer">
<div class="time">{timeFilter(state.time || 0)}</div>
<div class="buttons">
<button onClick={this.start}>Start</button>
<button>Pause</button>
<button>Reset</button>
</div>
</div>
);
}
}

View File

@ -1,93 +0,0 @@
import markoWidgets from 'marko-widgets'
import template from './template.marko'
import timeFilter from '../../util/time-filter'
const length = 25 * 60 * 1000
export default markoWidgets.defineComponent({
template,
componentDidMount() {
this.reset()
},
start(e) {
this.setState({
startTime: Date.now()
})
this.interval = setInterval(this.updateTimer.bind(this), 10)
},
pause(e) {
clearInterval(this.interval)
},
reset(e) {
clearInterval(this.interval)
this.setState({
time: length,
totalTime: length,
startTime: undefined
})
},
updateTimer() {
let time = this.state.totalTime - (Date.now() - this.state.startTime)
if (time <= 0) {
this.pause()
this.end()
time = 0
}
document.title = timeFilter(time).slice(0, 5)
this.setState({
time
})
},
end(time) {
const data = {
startTime: this.state.startTime,
endTime: new Date()
}
document.title = 'Pling!'
if (Notification.permission !== 'granted')
Notification.requestPermission()
else {
const notification = new Notification('Pomodoro complete!', {
icon: 'https://www.planetnatural.com/wp-content/uploads/2014/03/tomato-supplies.jpg',
body: 'Time for a break!',
})
}
fetch('/api/pomodoros', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then((response) => response.json())
.then((json) => {
console.log('succes')
})
},
getInitialState(input) {
return {
time: length,
totalTime: length,
startTime: undefined
}
}
})

View File

@ -1,11 +0,0 @@
<script marko-init>
const time = require('../../util/time-filter');
</script>
<div class="timer" w-bind>
<div class="time">${time(data.time || 0)}</div>
<div class="buttons">
<button w-onClick="start">Start</button>
<button>Pause</button>
<button>Reset</button>
</div>
</div>

5
client/error.jsx Normal file
View File

@ -0,0 +1,5 @@
export default function () {
<section>
<h1>Error!</h1>
</section>
}

View File

@ -1,3 +0,0 @@
<section>
<h1>Error!</h1>
</section>

View File

@ -1,3 +0,0 @@
{
"tags-dir": [ "./components" ]
}

25
client/master.jsx Normal file
View File

@ -0,0 +1,25 @@
import Layout from './components/Layout.jsx';
export default ({ css, js }) => (
'<!doctype html>' + (
<html lang="en">
<head>
<title>Pomodoro</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="robots" content="index, follow" />
<meta name="description" content="Beta Pomodoro timer that stores your pomodors." />
<meta name="keywords" content="pomodoro,gtd,productivity" />
<meta name="author" content="Linus Miller" />
<link type="text/css" rel="stylesheet" href={`/css/public${css ? css.suffix : ''}.css`} />
</head>
<body>
<Layout />
<script src={`/js/app${js ? js.suffix : ''}.js`} />
</body>
</html>
)
);

View File

@ -1,31 +0,0 @@
<!doctype html>
<html xml:lang="sv" lang="sv">
<head>
<title>Pomodoro</title>
<!-- Meta Tags -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="robots" content="index, follow" />
<meta name="description" content="Beta Pomodoro timer that stores your pomodors." />
<meta name="keywords" content="pomodoro,gtd,productivity" />
<meta name="author" content="Linus Miller" />
<!-- CSS -->
<link rel="stylesheet" href="/css/main${data.css && data.css.suffix}.css">
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="css/default-IE.css" />
<![endif]-->
</head>
<body>
<main>
<h1>Pomodoro Time!</h1>
<Timer />
</main>
<script src="/js/app${data.js && data.js.suffix}.js"></script>
</body>
</html>

View File

@ -1,7 +0,0 @@
window.process = {
browser: true,
env: {},
nextTick: (fnc) => {
setTimeout(fnc)
}
}

View File

@ -1,45 +0,0 @@
export default {
'': {
name: '',
//view: PageView,
template: require('./pages/index.marko'),
//subviews: {
// newsletterForm: [ '.newsletter', require('./views/newsletter-form') ]
//}
widget: require('./pages/index')
},
'samarbetspartners': {
//view: 'Page'
},
'om-oss': {
name: 'om-oss',
template: require('./pages/om-oss.marko'),
//view: PageView,
widget: require('./pages/om-oss')
},
'nyhetsbrev': {
name: 'nyhetsbrev',
template: require('./pages/nyhetsbrev.marko'),
//view: PageView,
widget: require('./pages/nyhetsbrev'),
//subviews: {
// newsletterForm: [ '.newsletter-form', require('./components/newsletter-form/view') ]
//},
},
'kontakt': {
name: 'kontakt',
//view: PageView,
template: require('./pages/kontakt.marko'),
widget: require('./pages/kontakt'),
//subviews: {
// googleMap: [ '#map-canvas', require('./views/google-map') ],
// contactForm: [ '.contact-form', require('./components/contact-form/view') ],
// newsletterForm: [ '.nyhetsbrev', require('./views/newsletter-form') ]
//}
}
};

View File

@ -1,17 +1,17 @@
const divs = [ 60, 100, 10 ]
const divs = [60, 100, 10];
export default function (time) {
const arr = []
const arr = [];
for (let i = 0; i < divs.length; i++) {
const nbr = divs.slice(i).reduce((a, b) => a * b)
const nbr = divs.slice(i).reduce((a, b) => a * b);
const result = Math.floor(time / nbr)
const result = Math.floor(time / nbr);
arr.push(result)
arr.push(result);
time = time - result * nbr
time = time - result * nbr;
// this.timerElements[i].textContent = result
}
return arr
return arr;
}

View File

@ -1,8 +1,8 @@
import splitTime from './split-time'
import twoDigits from './two-digits'
import splitTime from './split-time';
import twoDigits from './two-digits';
export default function (time) {
const arr = splitTime(time).map(twoDigits)
const arr = splitTime(time).map(twoDigits);
return arr.join(':')
return arr.join(':');
}

View File

@ -1,3 +1,3 @@
export default function twoDigits(val) {
return val >= 10 ? val : '0' + val
return val >= 10 ? val : `0${val}`;
}

2
gulp

@ -1 +1 @@
Subproject commit f69b4494ab2bc55edb09d503396f94bf8b457496
Subproject commit 349bb1f3a91a4675b5ec3e8b907d8ae2daf39129

13
gulpconfig.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
// modules > native
module.exports = {
rollup: {
entries: [
'client/app.jsx',
],
outputs: [
'app.js',
],
},
};

View File

@ -1 +1 @@
gulp/index.js
gulp/gulpfile.js

View File

@ -1,12 +1,12 @@
{
"name": "budbilar",
"name": "pomodoro",
"version": "0.0.1",
"description": "När du vill få något budat till rätt pris.",
"main": "server/server.js",
"private": true,
"repository": {
"type": "git",
"url": "git@gitlab.thecodebureau.com:axel/budbilar.git"
"url": "git@gitlab.bitmill.co:lohfu/pomodoro.git"
},
"scripts": {
"gulp": "gulp",
@ -16,67 +16,30 @@
"start": "node server/server.js"
},
"dependencies": {
"babel-preset-es2015-node6": "^0.3.0",
"babel-register": "^6.14.0",
"babel-preset-es2015-node6": "^0.4.0",
"babel-register": "^6.18.0",
"body-parser": "^1.15.2",
"chalk": "^1.1.3",
"connect-redis": "^3.1.0",
"cookie-parser": "^1.4.3",
"dollr": "0.0.9",
"dollr": "^0.1.1",
"express": "^4.14.0",
"express-service-errors": "github:thecodebureau/express-service-errors",
"express-session": "^1.14.1",
"lodash": "^4.15.0",
"marko": "^3.10.1",
"marko-widgets": "^6.3.4",
"midwest": "0.0.2",
"midwest-module-membership": "github:thebitmill/midwest-module-membership",
"midwest-service-errors": "github:thebitmill/midwest-service-errors",
"mongoose": "^4.6.0",
"express-session": "^1.14.2",
"hyperscript-jsx": "0.0.2",
"jsx-node": "^0.1.0",
"lodash": "^4.16.6",
"lowline": "^0.1.1",
"midwest": "^0.2.1",
"midwest-module-membership": "^0.1.0",
"midwest-service-errors": "^0.0.3",
"mongoose": "^4.6.6",
"mongopot": "github:lohfu/mongopot",
"morgan": "^1.7.0",
"nodemailer": "^2.6.0",
"nodemailer": "^2.6.4",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"require-dir": "^0.3.0"
},
"devDependencies": {
"autoprefixer": "^6.4.1",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"browser-sync": "^2.16.0",
"chalk": "^1.1.3",
"csswring": "^5.1.0",
"eslint": "^3.5.0",
"eslint-config-airbnb": "^11.1.0",
"eslint-config-airbnb-base": "^7.1.0",
"eslint-plugin-import": "^1.15.0",
"eslint-plugin-jsx-a11y": "^2.2.2",
"eslint-plugin-react": "^6.2.2",
"event-stream": "^3.3.4",
"gulp": "github:gulpjs/gulp#4.0",
"gulp-less": "^3.1.0",
"gulp-postcss": "^6.2.0",
"gulp-rename": "^1.2.2",
"gulp-sourcemaps": "^1.6.0",
"gulp-svgmin": "^1.2.2",
"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.7",
"marko": "^3.10.1",
"mkdirp": "^0.5.1",
"nodemon": "^1.10.2",
"postcss": "^5.2.0",
"pretty-hrtime": "^1.0.2",
"rimraf": "^2.5.4",
"rollup": "^0.35.10",
"rollup-plugin-babel": "^2.6.1",
"rollup-plugin-commonjs": "^3.3.0",
"rollup-plugin-marko": "0.0.2",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-replace": "^1.1.1",
"through2": "^2.0.1",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
"preact": "^6.4.0",
"require-dir": "^0.3.1"
}
}

24
server/.eslintrc Normal file
View File

@ -0,0 +1,24 @@
{
"extends": [
"airbnb-base",
"../.eslintrc"
],
"parserOptions": {
"sourceType": "strict"
},
"env": {
"node": true
},
"rules": {
"global-require": 0
},
"globals": {
"ENV": true,
"LOGIN_USER": true,
"PWD": true
}
}

View File

@ -1,7 +1,7 @@
'use strict'
'use strict';
const p = require('path')
const p = require('path');
module.exports = {
static: p.join(PWD, 'public'),
}
};

View File

@ -1,47 +1,47 @@
'use strict'
'use strict';
const _ = require('lodash')
const _ = require('lodash');
const errorTemplate = require('../templates/error.marko')
const errorTemplate = require('../../build/error.jsx');
const defaults = {
post: (req, res, next) => {
res.template = errorTemplate
res.template = errorTemplate;
next()
next();
},
mystify: {
properties: ['errors', 'message', 'name', 'status', 'statusText']
properties: ['errors', 'message', 'name', 'status', 'statusText'],
},
log: {
// if database = true there has to be a mongoose model name ErrorModel
ignore: [],
}
}
},
};
const ErrorModel = require('midwest-service-errors/model')
const ErrorModel = require('midwest-service-errors/model');
function store(error) {
ErrorModel.create(error, (err) => {
// TODO handle errors in error handler better
if (err) {
console.error('ERROR WRITING TO DATABASE')
console.error(err)
console.log(err.errors)
console.error('ORIGINAL ERROR')
console.error(error)
console.error('ERROR WRITING TO DATABASE');
console.error(err);
console.error(err.errors);
console.error('ORIGINAL ERROR');
console.error(error);
}
})
});
}
module.exports = _.merge(defaults, {
development: {
log: {
store: store,
store,
console: true,
}
},
},
testing: {
log: {
@ -51,8 +51,8 @@ module.exports = _.merge(defaults, {
},
production: {
log: {
store: store,
store,
console: false,
}
},
}[ENV])
},
}[ENV]);

View File

@ -1,4 +1,4 @@
'use strict'
'use strict';
global.LOGIN_USER = 'lohfu@lohfu.io'
global.LOGIN_USER = 'lohfu@lohfu.io';
// global.LOGIN_USER = 'zarac@zarac.se'

View File

@ -1,33 +1,34 @@
'use strict'
'use strict';
const config = {
site: require('./site')
}
site: require('./site'),
};
module.exports = {
invite: {
from: config.site.title + ' Robot <' + config.site.emails.robot + '>',
subject: 'You have been invited to ' + config.site.title
from: `${config.site.title} Robot <${config.site.emails.robot}>`,
subject: `You have been invited to ${config.site.title}`,
},
timeouts: {
// 1 day
changePassword: 24 * 60 * 60 * 1000,
// verify email
verifyEmail: 7 * 24 * 60 * 60 * 1000
verifyEmail: 7 * 24 * 60 * 60 * 1000,
},
paths: {
register: '/register',
login: '/login',
forgotPassword: '/forgot-password',
updatePassword: '/change-password'
register: '/admin/register',
login: '/admin/login',
forgotPassword: '/admin/forgot-password',
updatePassword: '/admin/change-password',
verifyEmail: '/admin/verify-email',
},
redirects: {
login: '/admin',
logout: '/',
register: '/admin'
register: '/admin',
},
remember: {
@ -46,19 +47,19 @@ module.exports = {
externalLoginFailed: 'External login failed.',
unverified: 'This account has not been verified.',
banned: 'User is banned.',
blocked: 'User is blocked due to too many login attempts.'
blocked: 'User is blocked due to too many login attempts.',
},
register: {
missingProperties: 'Oh no missing stuff',
notAuthorized: 'The email is not authorized to create an account.',
duplicateEmail: 'The email has already been registered.'
}
duplicateEmail: 'The email has already been registered.',
},
},
passport: {
local: {
usernameField: 'email'
usernameField: 'email',
},
scope: ['email'],
@ -76,5 +77,5 @@ module.exports = {
// needs to be even
tokenLength: 64,
// needs to be even
saltLength: 16
}
saltLength: 16,
};

View File

@ -1,11 +1,11 @@
'use strict'
'use strict';
const defaults = {
uri: 'mongodb://pomodoro-supreme:lets-work-our-asses-off-poop-poop@mongo.bitmill.co/pomodoro'
}
uri: 'mongodb://pomodoro-supreme:lets-work-our-asses-off-poop-poop@mongo.bitmill.co/pomodoro',
};
module.exports = Object.assign(defaults, {
production: {
// uri: 'mongodb://pomodoro-supreme:lets-work-our-asses-off-poop-poop@localhost/pomodoro'
}
}[ENV])
},
}[ENV]);

View File

@ -1,10 +1,10 @@
'use strict'
'use strict';
const basePort = 3050
const basePort = 3050;
module.exports = {
development: basePort,
testing: basePort + 1,
staging: basePort + 2,
production: basePort + 3
}[ENV]
production: basePort + 3,
}[ENV];

View File

@ -1,37 +1,38 @@
'use strict'
'use strict';
const session = require('express-session')
const chalk = require('chalk');
const session = require('express-session');
let redisStore
let redisStore;
const config = {
secret: 'asdf1h918798&(*&ijh21kj4hk123j45h2k34jh52k3g45)thisisacompletelyrandomgeneratedstring...whatastrangecoincidence...',
secret: 'sometimespoopfeelslikeheaveninawarmbun.:L:AKJSHFKJh12349087ashabbasbasbbasdbfas w',
resave: false,
saveUninitialized: true
}
saveUninitialized: true,
};
const redisConfig = {
host: 'localhost',
port: 6379
}
port: 6379,
};
if (ENV === 'production') {
const RedisStore = require('connect-redis')(require('express-session'))
const RedisStore = require('connect-redis')(require('express-session'));
redisStore = new RedisStore(redisConfig)
redisStore = new RedisStore(redisConfig);
redisStore.on('connect', function () {
console.info('Redis connected succcessfully')
})
redisStore.on('connect', () => {
console.info(`[${chalk.cyan('INIT')}] Redis connected succcessfully`);
});
redisStore.on('disconnect', function () {
throw new Error('Unable to connect to redis. Has it been started?')
})
redisStore.on('disconnect', () => {
throw new Error('Unable to connect to redis. Has it been started?');
});
config.store = redisStore
config.store = redisStore;
} else {
config.store = new session.MemoryStore()
config.store = new session.MemoryStore();
}
module.exports = config
module.exports = config;

59
server/config/shim.js Normal file
View File

@ -0,0 +1,59 @@
'use strict';
module.exports = {
'https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js': [
'chrome <= 12',
'chrome mobile <= 12',
'chrome mobile ios <= 12',
'firefox <= 20',
'firefox mobile <= 20',
'ie <= 9',
'ie mobile <= 9',
'opera <= 12',
'safari <= 5',
'mobile safari <= 5',
'samsung internet <= 1',
],
'https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.min.js': [
'chrome <= 51',
'chrome mobile <= 51',
'chrome mobile ios <= 51',
'edge <= 13',
'edge mobile <= 13',
'firefox <= 44',
'firefox mobile <= 44',
'ie <= 11',
'ie mobile <= 11',
'safari <= 9',
'mobile safari <= 9',
'samsung internet <= 2',
],
'https://unpkg.com/es7-shim@latest/dist/es7-shim.min.js': [
'chrome <= 50',
'chrome mobile <= 50',
'chrome mobile ios <= 50',
'edge <= 14',
'edge mobile <= 14',
'firefox <= 46',
'firefox mobile <= 46',
'ie <= 11',
'ie mobile <= 11',
'safari <= 10',
'mobile safari <= 10',
'samsung internet <= 3',
],
'https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js': [
'chrome <= 41',
'chrome mobile <= 41',
'chrome mobile ios <= 41',
'edge <= 13',
'edge mobile <= 13',
'firefox <= 38',
'firefox mobile <= 38',
'ie <= 11',
'ie mobile <= 11',
'safari <= 9',
'mobile safari <= 9',
'samsung internet <= 3',
],
};

View File

@ -1,51 +1,51 @@
'use strict'
'use strict';
const _ = require('lodash')
const _ = require('lodash');
const domain = 'pomodoro.bitmill.co'
const domain = 'pomodoro.bitmill.co';
const defaults = {
domain: domain,
domain,
title: 'Pomodoro',
name: 'pomodoro',
name: 'pomdoro',
protocol: 'http',
get host() {
return this.port ? this.hostname + ':' + this.port : this.hostname
return this.port ? `${this.hostname}:${this.port}` : this.hostname;
},
get url() {
return this.protocol + '://' + this.host + '/'
return `${this.protocol}://${this.host}/`;
},
emails: {
robot: 'no-reply@thecodebureau.com',
info: 'info@thecodebureau.com',
webmaster: 'webmaster@thecodebureau.com',
order: 'info@thecodebureau.com'
}
}
order: 'info@thecodebureau.com',
},
};
module.exports = _.merge(defaults, {
development: {
hostname: 'localhost',
port: process.env.EXTERNAL_PORT || process.env.PORT || require('./port')
port: process.env.EXTERNAL_PORT || process.env.PORT || require('./port'),
},
testing: {
hostname: 'localhost',
port: process.env.PORT || require('./port')
port: process.env.PORT || require('./port'),
},
staging: {
hostname: 'staging.' + domain
hostname: 'staging.pomodoro.bitmill.co',
},
production: {
hostname: domain,
protocol: 'https',
emails: {
robot: 'no-reply@bitmill.co',
info: 'info@bitmill.co',
webmaster: 'webmaster@bitmill.co',
order: 'order@bitmill.co'
}
}
}[ENV])
// protocol: 'https',
// emails: {
// robot: `no-reply@${domain}`,
// info: `info@${domain}`,
// webmaster: `webmaster@${domain}`,
// order: `order@${domain}`,
// },
},
}[ENV]);

View File

@ -1,15 +1,15 @@
'use strict'
'use strict';
const _ = require('lodash')
const _ = require('lodash');
const defaults = {
auth: {
user: 'SMTP_Injection',
// dev key
pass: '2eec390c5b3f5d593c9f152179bf51e90b073784'
pass: '2eec390c5b3f5d593c9f152179bf51e90b073784',
},
host: 'smtp.sparkpostmail.com',
port: 587
}
port: 587,
};
module.exports = _.merge(defaults, {}[ENV])
module.exports = _.merge(defaults, {}[ENV]);

View File

@ -1,13 +0,0 @@
'use strict'
const masterTemplate = require('../client/master.marko')
const router = new (require('express')).Router()
router.get('/', (req, res, next) => {
res.template = masterTemplate
next()
})
module.exports = router

31
server/render.jsx Normal file
View File

@ -0,0 +1,31 @@
module.exports = function (Component, Master) {
const locals = Object.assign({}, this.app.locals, this.locals);
let preamble = '';
let html;
if (typeof Master === 'function') {
preamble = '<!doctype html>';
if (typeof Component === 'function') {
return this.send(preamble + (
<Master {...locals}>
<Component {...locals}/>
</Master>
));
}
Component = Master;
}
if (typeof Component !== 'function') {
throw new Error('Not a Component');
} else if (Component.prototype && Component.prototype.render) {
const instance = new Component(locals);
html = instance.render(instance.props, instance.state);
} else {
html = Component(locals);
}
this.send(preamble ? preamble + html : html);
};

13
server/routers/index.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
const masterTemplate = require('../../build/master.jsx');
const router = new (require('express')).Router();
router.get('/', (req, res, next) => {
res.template = masterTemplate;
next();
});
module.exports = router;

View File

@ -1,6 +1,5 @@
'use strict'
'use strict';
require('babel-register')
/*
* The main file that sets up the Express instance and node
*
@ -9,58 +8,55 @@ require('babel-register')
*/
// set up some globals (these are also set in Epiphany if not already set)
global.ENV = process.env.NODE_ENV || 'development'
global.PWD = process.env.NODE_PWD || process.cwd()
// make node understand `*.marko` files
require('marko/node-require').install()
global.ENV = process.env.NODE_ENV || 'development';
global.PWD = process.env.NODE_PWD || process.cwd();
// modules > native
const p = require('path')
const p = require('path');
if (ENV === 'development') {
// output filename in console log and colour console.dir
require('midwest/util/console');
// needed so symlinked modules get access to main projects node_modules/
require('app-module-path').addPath(p.join(PWD, 'node_modules'));
}
// make node understand `*.jsx` files
require('jsx-node').install({
replace: {
lowline: 'lodash',
preact: 'jsx-node',
},
});
// modules > 3rd party
const requireDir = require('require-dir')
const _ = require('lodash')
const express = require('express')
const mongoose = require('mongoose')
const passport = require('passport')
const _ = require('lodash');
const chalk = require('chalk');
const express = require('express');
const mongoose = require('mongoose');
const passport = require('passport');
const requireDir = require('require-dir');
// modules > express middlewares
const bodyParser = require('body-parser')
const session = require('express-session')
const cookieParser = require('cookie-parser')
// modules > 3rd party
const chalk = require('chalk')
const bodyParser = require('body-parser');
const session = require('express-session');
const cookieParser = require('cookie-parser');
// modules > midwest
const colorizeStack = require('midwest/util/colorize-stack')
const colorizeStack = require('midwest/util/colorize-stack');
const config = requireDir('./config')
const config = requireDir('./config', { camelcase: true });
// make error output stack pretty
process.on('uncaughtException', (err) => {
console.error(chalk.red('UNCAUGHT EXCEPTION'))
console.error(chalk.red('UNCAUGHT EXCEPTION'));
if (err.stack) {
console.error(colorizeStack(err.stack))
console.error(colorizeStack(err.stack));
} else {
console.error(err)
console.error(err);
}
process.exit(1)
})
// mongoose mpromise library is being deprecated
mongoose.Promise = Promise
// connect to mongodb
mongoose.connect(config.mongo.uri, _.omit(config.mongo, 'uri'), (err) => {
if (err) {
console.error(err)
process.exit()
}
console.info('[' + chalk.cyan('INIT') + '] Mongoose is connected.')
})
process.exit(1);
});
const prewares = [
express.static(config.dir.static, ENV === 'production' ? { maxAge: '1 year' } : null),
@ -70,70 +66,86 @@ const prewares = [
session(config.session),
passport.initialize(),
passport.session(),
]
];
if (ENV === 'development') {
// only log requests to console in development mode
prewares.unshift(require('morgan')('dev'))
prewares.push(require('midwest-module-membership/passport/automatic-login'))
prewares.unshift(require('morgan')('dev'));
// automatically login global.LOGIN_USER
prewares.push(require('midwest-module-membership/passport/automatic-login'));
}
const postwares = [
require('midwest/middleware/ensure-found'),
// transform and log error
require('midwest/middleware/error-handler'),
require('midwest/factories/error-handler')(config.errorHandler),
// respond
require('midwest/middleware/responder'),
]
];
const server = express()
const server = express();
// get IP & whatnot from nginx proxy
server.set('trust proxy', true)
server.set('trust proxy', true);
server.locals.site = require('./config/site')
if (ENV === 'production') {
Object.assign(server.locals, {
js: require(p.join(PWD, 'public/js.json')),
css: require(p.join(PWD, 'public/css.json'))
})
}
site: require('./config/site'),
});
// override default response render method for
// more convenient use with marko
server.response.render = function (template) {
const locals = _.extend({}, server.locals, this.locals)
// more convenient use with jsx
server.response.render = require('./render.jsx');
template.render(locals, this)
}
try {
server.locals.js = require(p.join(PWD, 'public/js.json'));
} catch (e) {}
try {
server.locals.css = require(p.join(PWD, 'public/css.json'));
} catch (e) {}
// load all prewares
server.use(...prewares)
server.use(...prewares);
// routes > pages
server.use(require('./pages'))
server.use(require('./routers/index'));
// routes > authentication
server.use('/auth', require('midwest-module-membership/passport/router'))
// routes > api > membership
server.use('/api/roles', require('midwest-module-membership/services/roles/router'))
server.use('/api/permissions', require('midwest-module-membership/services/permissions/router'))
server.use('/api/invites', require('midwest-module-membership/services/invites/router'))
server.use('/api/users', require('midwest-module-membership/services/users/router'))
// routes > api > errors
server.use('/api/errors', require('midwest-service-errors/router'))
server.use('/auth', require('midwest-module-membership/passport/router'));
// routes > api > pomodoro
server.use('/api/pomodoros', require('./services/pomodoros/router'))
server.use('/api/pomodoros', require('./services/pomodoros/router'));
// routes > api > errors
server.use('/api/errors', require('midwest-service-errors/router'));
// routes > api > membership
server.use('/api/roles', require('midwest-module-membership/services/roles/router'));
server.use('/api/permissions', require('midwest-module-membership/services/permissions/router'));
server.use('/api/invites', require('midwest-module-membership/services/invites/router'));
server.use('/api/users', require('midwest-module-membership/services/users/router'));
// load all postwares
server.use(...postwares)
server.use(...postwares);
server.listen(config.port, () => {
console.info('[' + chalk.cyan('INIT') + '] HTTP Server listening on port ' + chalk.magenta('%s') + ' (' + chalk.yellow('%s') + ')', config.port, ENV)
})
// mpromise (built in mongoose promise library) is deprecated,
// tell mongoose to use native Promises instead
mongoose.Promise = Promise;
// connect to mongodb
mongoose.connect(config.mongo.uri, _.omit(config.mongo, 'uri'), (err) => {
if (err) {
console.error(err);
process.exit();
}
module.exports = server
console.info(`[${chalk.cyan('INIT')}] Mongoose is connected.`);
});
// Only start Express server when it is the main module (ie not required by test)
if (require.main === module) {
server.http = server.listen(config.port, () => {
console.info(`[${chalk.cyan('INIT')}] HTTP Server listening on port ${chalk.magenta(config.port)} (${chalk.yellow(server.get('env'))})`);
});
}
module.exports = server;

View File

@ -1,48 +1,48 @@
'use strict'
'use strict';
const rest = require('midwest/middleware/rest')
const formatQuery = require('midwest/middleware/format-query')
const paginate = require('midwest/middleware/paginate')
const rest = require('midwest/factories/rest');
const formatQuery = require('midwest/factories/format-query');
const paginate = require('midwest/factories/paginate');
const Pomodoro = require('./model')
const Pomodoro = require('./model');
function create(req, res, next) {
Pomodoro.create(Object.assign(req.body, {
user: req.user && req.user.id || '57b04f50a1eaaf354f3b96a6',
ip: req.ip,
userAgent: req.headers['user-agent']
userAgent: req.headers['user-agent'],
}), (err, pomodoro) => {
res.locals.pomodoro = pomodoro
res.locals.pomodoro = pomodoro;
next(err)
})
next(err);
});
}
function end(req, res, next) {
Pomodoro.findByIdAndUpdate(req.params.id, { endDate: new Date() }, (err, pomodoro) => {
if (err) return next(err)
if (err) return next(err);
res.locals.pomodoro = pomodoro
res.locals.pomodoro = pomodoro;
next()
})
next();
});
}
function getActive(req, res, next) {
Pomodoro.find({ user: req.user.id, endDate: { $eq: null } }, (err, pomodoros) => {
res.locals.pomodoros = pomodoros
res.locals.pomodoros = pomodoros;
return next(err)
})
return next(err);
});
}
module.exports = Object.assign(rest(Pomodoro), {
create,
end,
formatQuery: formatQuery(['limit', 'sort'], {
endDate: 'exists'
endDate: 'exists',
}),
getActive,
paginate: paginate(Pomodoro, 50)
})
paginate: paginate(Pomodoro, 50),
});

View File

@ -1,31 +1,31 @@
'use strict'
'use strict';
const mongoose = require('mongoose')
const mongoose = require('mongoose');
const PomodoroSchema = new mongoose.Schema({
startTime: {
type: Date,
required: true
required: true,
},
endTime: {
type: Date,
required: true
required: true,
},
name: String,
location: String,
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
required: true,
},
ip: {
type: String,
required: true
required: true,
},
userAgent: {
type: String,
required: true
required: true,
},
})
});
module.exports = mongoose.model('Pomodoro', PomodoroSchema)
module.exports = mongoose.model('Pomodoro', PomodoroSchema);

View File

@ -1,19 +1,19 @@
'use strict'
'use strict';
const router = new (require('express')).Router()
const router = new (require('express')).Router();
const mw = require('./middleware')
const mw = require('./middleware');
const { isAuthenticated } = require('midwest-module-membership/passport/authorization-middleware')
const { isAuthenticated } = require('midwest-module-membership/passport/authorization-middleware');
router.route('/')
.get(isAuthenticated, mw.formatQuery, mw.paginate, mw.query)
.post(isAuthenticated, mw.create)
.post(isAuthenticated, mw.create);
router.route('/:id')
.get(isAuthenticated, mw.findById)
.patch(isAuthenticated, mw.update)
.put(isAuthenticated, mw.replace)
.delete(isAuthenticated, mw.remove)
.delete(isAuthenticated, mw.remove);
module.exports = router
module.exports = router;