Skip to content

Commit

Permalink
Merge 299c983 into cb8065b
Browse files Browse the repository at this point in the history
  • Loading branch information
osteele committed Jun 11, 2018
2 parents cb8065b + 299c983 commit 19184d4
Show file tree
Hide file tree
Showing 19 changed files with 320 additions and 143 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ rules:

overrides:
# server:
- files: [ "index.html.js", "server.js", "webpack.config.js" ]
- files: [ "server/*.js", "server.js", "sslify.js", "webpack.config.js" ]
parser: esprima
env:
browser: false
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"expose": "^0.1.4",
"expose-loader": "^0.7.3",
"express": "^4.15.3",
"helmet": "^3.12.1",
"http-server": "0.10.0",
"input-moment": "git+https://github.com/kylecombes/input-moment.git",
"lodash": "^4.17.10",
Expand Down Expand Up @@ -97,7 +98,10 @@
"webpack-dev-server": "^2.5.0"
},
"jest": {
"coveragePathIgnorePatterns": ["/node_modules/","__test__"],
"coveragePathIgnorePatterns": [
"/node_modules/",
"__test__"
],
"moduleNameMapper": {
"\\.(svg)$": "<rootDir>/__mocks__/fileMock.js"
},
Expand Down
20 changes: 19 additions & 1 deletion public/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -772,4 +772,22 @@ textarea.markdown-editor {
100% {
transform: rotate(360deg);
}
}
}

.messages {
z-index: 1000;
width: 100%;
background: yellow;
padding: 5px 15px;
position: absolute;
transition: transform 0.5s;
transform: scaleY(1);
}

.messages.count-0 {
transform: scaleY(0);
}

.messages.count-0 .closebox {
display: none;
}
41 changes: 29 additions & 12 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
const fs = require('fs');
const bodyParser = require('body-parser');
const express = require('express');
const helmet = require('helmet');
const sslify = require('./server/sslify');

const PORT = process.env.PORT || 8080;
const { FORCE_HTTPS } = process.env;

const app = express();
const bodyParser = require('body-parser');

const port = process.env.PORT || 8080;
const fs = require('fs');
app.use(helmet({
// HSTS is more trouble than it's worth (in our use case), since it wreaks
// havoc if accidentally enabled in the wrong environment.
hsts: false,
}));
if (FORCE_HTTPS) {
app.use(sslify);
}

app.use(bodyParser.json({ type: 'application/*+json' }));
app.use(express.static(__dirname));

// Check if we're running on a local dev machine
// The presence of `.env` signals to run in development mode.
//
// In this mode, the server loads environment variables from `.env` and runs
// webpack-dev-server as middleware.
//
// Otherwise it depeends on a prior webpack build step.
if (fs.existsSync('./.env')) {
// Load environment variables from .env
require('dotenv').config();
Expand All @@ -21,23 +38,23 @@ if (fs.existsSync('./.env')) {
const compiler = webpack(webpackConfig);

app.use(middleware(compiler, {
// webpack-dev-middleware options
// Options from https://github.com/webpack/webpack-dev-middleware#options
}));
}

const getHtml = require('./index.html.js');
// Ugly HTML template TODO: Do this better
const html = getHtml();
const html = fs
.readFileSync('./public/index.html', 'utf-8')
.replace(/\bsrc="\/public\/build(\/(.*\.)?bundle.js)"/g, 'src="$1"');

app.get('*', (req, res) => {
res.send(html);
});

const server = app.listen(port, () => {
let host = server.address().address;
const server = app.listen(PORT, () => {
const { address, port } = server.address();
// replace IPv6 wildcard by a recognizable URL, that can be used in a browser
// address bar
host = host.replace(/^::$/, '0.0.0.0');
const host = address.replace(/^::$/, '0.0.0.0');
// Printed thus, some terminals display a clickable link
console.log('Dev server is listening at http://%s:%s/', host, server.address().port);
console.log(`Server is listening at http://${host}:${port}/`);
});
20 changes: 20 additions & 0 deletions server/sslify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ACME (Let's Encrypt) requires HTTP access to paths with this prefix.
const ACME_CHALLENGE_PATH = '/.well-known/acme-challenge/';

// skip rewriting this request?
const skipRedirect = (req) => {
const remoteProto = req.headers['x-forwarded-proto'];
// Prefer the header over req.secure. The client->lb connection differ in
// either direction from the lb -> server connection.
const proto = remoteProto || (req.secure ? 'https' : 'https');
// Avoid recursive redirects, and forwarding ACME challenges.
return proto === 'https' || req.path.startsWith(ACME_CHALLENGE_PATH);
};

// rewrite this request's URL as HTTPS
const rewrite = req => `https://${req.get('Host')}${req.url}`;

// ExpressJS middleware
const sslify = (req, res, next) => (skipRedirect(req) ? next() : res.redirect(rewrite(req)));

module.exports = sslify;
4 changes: 2 additions & 2 deletions src/__test__/add-edit-page.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import AddEditEventPage from '../pages/add-edit/add-edit-page';

describe('AddEditEventPage', () => {
const userAccount = {
scope: new Map(),
scope: new Set(),
};
const adminAccount = {
scope: new Map(),
scope: new Set(),
};
const event = {
title: 'An event title',
Expand Down
28 changes: 25 additions & 3 deletions src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import LabelsContainer from './containers/labels-container';
import SidebarContainer from './containers/sidebar-container';
import SubscriptionContainer from './containers/subscription-container';
import { withAccountInfo } from './containers/with-server-data';
import { fetchLabels, fetchAccessInfo, toggleSidebarCollapsed } from './data/actions';
import {
clearMessages,
fetchAccessInfo,
fetchLabels,
toggleSidebarCollapsed,
} from './data/actions';
import { initializeAccessToken } from './data/auth';
import setupStore from './data/setup-store';

Expand All @@ -27,8 +32,14 @@ initializeAccessToken();
store.dispatch(fetchAccessInfo());
store.dispatch(fetchLabels());

const App = () => (
const App = ({ messages, onMessageClose }) => (
<div className="app-container">
<div className={`messages count-${messages.length}`}>
<span className="closebox big ion-ios-close" onClick={onMessageClose}>
&nbsp;
</span>
{messages.map(({ id, message }) => <p key={id}>{message}</p>)}
</div>
<SidebarContainer />
<div className="scene-container">
<div className="scene-overlay" onClick={() => store.dispatch(toggleSidebarCollapsed())} />
Expand All @@ -50,9 +61,20 @@ const App = () => (
// Guard the entire app, in order to get a single loading indicator instead of
// one for the sidebar and another for the main calendar view
const mapStateToProps = state => ({
messages: state.messages,
user: state.user,
});
const AppContainer = connect(mapStateToProps)(withAccountInfo(App));

const mapDispatchToProps = dispatch => ({
onMessageClose: (mode) => {
dispatch(clearMessages(mode));
},
});

const AppContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(withAccountInfo(App));

ReactDOM.render(
<Provider store={store}>
Expand Down
7 changes: 5 additions & 2 deletions src/containers/import-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const mapDispatchToProps = (dispatch, getState) => ({
setPageTitlePrefix: (title) => {
dispatch(setPageTitlePrefix(title));
},
importSuccess: (response, importData) => {
importSuccess: (importData) => {
ga.event({
category: 'ICS Feed Import',
variable: 'success',
Expand All @@ -57,6 +57,9 @@ const mapDispatchToProps = (dispatch, getState) => ({
});

// Connect props to Redux state and actions
const ImportContainer = connect(mapStateToProps, mapDispatchToProps)(ImportPage);
const ImportContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(ImportPage);

export default ImportContainer;
7 changes: 5 additions & 2 deletions src/containers/subscription-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const mapDispatchToProps = (dispatch, getState) => ({
setPageTitlePrefix: (title) => {
dispatch(setPageTitlePrefix(title));
},
importSuccess: (response, importData) => {
importSuccess: (importData) => {
ga.event({
category: 'Subscription Preferences',
variable: 'success',
Expand All @@ -51,6 +51,9 @@ const mapDispatchToProps = (dispatch, getState) => ({
});

// Connect props to Redux state and actions
const SubscriptionContainer = connect(mapStateToProps, mapDispatchToProps)(SubscriptionEditorPage);
const SubscriptionContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(SubscriptionEditorPage);

export default SubscriptionContainer;
46 changes: 23 additions & 23 deletions src/data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import moment from 'moment';
import ReactGA from 'react-ga';
import { push } from 'react-router-redux';
import { getAccessToken, setAccessTokenFromResponse } from './auth';
import apiClient from './client';
import { decodeEvent } from './encoding';
import { API_SERVER_URL, TOKEN_INFO_ENDPOINT } from './settings';
import { TOKEN_INFO_ENDPOINT } from './settings';

/* eslint-disable max-len */
export const ActionTypes = {
Expand Down Expand Up @@ -73,6 +74,10 @@ export function displayError(error, message) {
return { type: ActionTypes.DISPLAY_ERROR, error, message };
}

export function clearMessages() {
return { type: ActionTypes.CLEAR_MESSAGES };
}

// ----- End notification/message bar actions ----- //

// ----- Begin sidebar actions ----- //
Expand Down Expand Up @@ -280,12 +285,10 @@ export function setCurrentEventById(id, recId) {
dispatch(setCurrentEventData(eventData));
} else {
// We don't have the data, so request it from the server
const url = recId
? `${API_SERVER_URL}/events/${id}/${recId}`
: `${API_SERVER_URL}/events/${id}`;
axios
const url = recId ? `/events/${id}/${recId}` : `/events/${id}`;
apiClient
.get(url)
.then(response => dispatch(setCurrentEventData(response.data)))
.then(({ data }) => dispatch(setCurrentEventData(data)))
.catch(response => console.error(response)); // TODO: Display an error message to the user
}
};
Expand All @@ -312,13 +315,10 @@ export function refreshEvents(start, end) {
return (dispatch) => {
const startString = `${start.year()}-${start.month() + 1}-${start.date()}`;
const endString = `${end.year()}-${end.month() + 1}-${end.date()}`;
return axios
.get(`${API_SERVER_URL}/events/?start=${startString}&end=${endString}`)
.then(response => response.data, error => dispatch(displayError(error)))
.then((data) => {
const events = data.map(decodeEvent);
dispatch(setEvents(events));
});
return apiClient.get(`/events/?start=${startString}&end=${endString}`).then(({ data }) => {
const events = data.map(decodeEvent);
dispatch(setEvents(events));
});
};
}

Expand Down Expand Up @@ -382,10 +382,10 @@ export function deleteCurrentEvent() {
return (dispatch, getStore) => {
const store = getStore();
const event = store.events.current;
axios
.delete(`${API_SERVER_URL}/events/${event.id || event.sid}`)
.then(() => dispatch(eventDeletedSuccessfully(event.id || event.sid)))
.catch(response => eventDeleteFailed(event, response));
apiClient
.delete(`/events/${event.id || event.sid}`)
.catch(response => eventDeleteFailed(event, response))
.then(() => dispatch(eventDeletedSuccessfully(event.id || event.sid)));
};
}

Expand Down Expand Up @@ -515,10 +515,10 @@ export function refreshLabelsIfNeeded() {
*/
export function fetchLabels() {
return dispatch =>
axios
.get(`${API_SERVER_URL}/labels/`)
.then(response => response.data, error => dispatch(displayError(error)))
.then(labels => dispatch(setLabels(labels)));
apiClient
.get('/labels/')
.catch(error => dispatch(displayError(error)))
.then(({ data }) => dispatch(setLabels(data)));
}

/**
Expand Down Expand Up @@ -577,8 +577,8 @@ export function labelVisibilityToggled(labelName) {
export function updateLabel(data) {
return () => {
// TODO: update model on success
axios
.post(`${API_SERVER_URL}/labels/${data.id}`, data)
apiClient
.post(`/labels/${data.id}`, data)
.catch(error => alert(`Update label failed:\n${error}`));
};
}
Expand Down
8 changes: 8 additions & 0 deletions src/data/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import axios from 'axios';
import { API_SERVER_URL } from './settings';

const apiClient = axios.create({
baseURL: API_SERVER_URL,
});

export default apiClient;
Loading

0 comments on commit 19184d4

Please sign in to comment.