Skip to content
This repository has been archived by the owner on Mar 3, 2021. It is now read-only.

RSS configuration and a simple admin panel #96

Merged
merged 28 commits into from
Mar 14, 2018
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2f81e7a
Serve the podcast RSS feed from Shortcut server
dariusk Dec 31, 2017
7bf104e
Server and client can use RSS feed as data source
dariusk Jan 18, 2018
86d2f8f
Admin panel
dariusk Jan 30, 2018
a7560d1
security update per vulnerability alert
dariusk Jan 23, 2018
5cf456f
Add upstream repository to server/package.json
Echelon9 Jan 28, 2018
ea4b8ce
Remove stray Yeoman generator config file
Echelon9 Jan 28, 2018
5a8c678
client: Update client project name to abstract away from tal
Echelon9 Jan 28, 2018
d377be7
server: Update server project name to abstract away from tal
Echelon9 Jan 28, 2018
248ced6
Add nodemon to shortcut-server devDependencies
Echelon9 Jan 31, 2018
3bc0a34
Improve test coverage by enabling ClippingContainerComponent test
Echelon9 Feb 1, 2018
b9b4920
client: Add missing peer dependency flexboxgrid
Echelon9 Feb 6, 2018
7c5dbef
Additional error state on RSS endpoint
dariusk Feb 12, 2018
1663421
Server uses single global cache object
dariusk Feb 14, 2018
60be559
test server response for text vs parsed array
dariusk Feb 15, 2018
adff6cf
Display title instead of guid on admin pane
dariusk Feb 15, 2018
7ed397e
Fix make sure cache is always initialized with empty array
dariusk Feb 15, 2018
a1a8f2d
Make admin panel compatible with episodes.json
dariusk Feb 15, 2018
e8fe92b
Setting RSS to false as default so Getting Started docs work simply
dariusk Feb 15, 2018
0fddb27
Admin password controlled by .env file
dariusk Feb 15, 2018
67ddd35
Small changes based on @therewasaguy's feedback
dariusk Mar 13, 2018
caf94c9
Remove infinite scroll on landing page
dariusk Mar 13, 2018
7ee4cb4
Do not render Admin panel unless authenticated
dariusk Mar 13, 2018
fbdaa59
Remove confusing note
dariusk Mar 14, 2018
d80755b
Removing the '/rss' endpoint
dariusk Mar 14, 2018
7c1bc26
Cleaning up warnings
dariusk Mar 14, 2018
615dcd1
Remove some old logic/comments
dariusk Mar 14, 2018
e62cbc6
Admin will always update episode cache
dariusk Mar 14, 2018
8fecf48
Merge branch 'master' into 80-enable-rss
dariusk Mar 14, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions client/.yo-rc.json

This file was deleted.

2 changes: 1 addition & 1 deletion client/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# tal-client
# shortcut-client

This is the client-side React app. Most of these commands should work, but please refer to [the wiki](https://github.com/FeelTrainCoop/shortcut/wiki) for up-to-date documentation instead. This README will soon be updated to reflect the current state of the project.

Expand Down
11 changes: 8 additions & 3 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@
"express": "~4.13.4",
"express-session": "~1.13.0",
"fbgraph": "~1.1.0",
"flexboxgrid": "^6.3.0",
"hls.js": "~0.6.2-3",
"jquery": "~2.2.3",
"jquery": "^3.3.1",
"material-ui": "^1.0.0-beta.13",
"material-ui-icons": "^1.0.0-alpha.19",
"moment": "~2.14.1",
Expand Down
104 changes: 104 additions & 0 deletions client/src/components/Admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import { Paper } from 'material-ui';
import Switch from 'material-ui/Switch';
import { FormControlLabel, FormGroup } from 'material-ui/Form';

const parentSiteName = require('config').default.parentSiteName;
const logo = require('../images/logo.png');
const jQuery = require('jquery');

require('styles/Admin.scss');

class AdminComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
eps: props.eps,
switches: []
};
this.apiEndpoint = props.apiEndpoint;
}

componentDidMount() {
jQuery.when(
// get our stored list of what episodes are enabled/disabled
jQuery.ajax({
url: `${this.apiEndpoint}/admin/getEpisodes`,
xhrFields: { withCredentials: true },
}),
// get our list of all episodes, unfiltered since this is the admin pane
jQuery.ajax({
url: `${this.apiEndpoint}/recent?filter=0`,
})
).done(function (episodeStateData, allEpisodeData) {
let tempSwitches = allEpisodeData[0].map(episode => {
let foundElement = episodeStateData[0].find(el => el.value === episode.guid);
episode.checked = foundElement ? foundElement.enabled : false;
episode.value = episode.guid;
return episode;
});
this.setState({
switches: tempSwitches
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe set "authenticated" here?

});
}.bind(this));
}

renderSwitches() {
return this.state.switches
.map((el, index) =>
<FormControlLabel
control={
<Switch
checked={el.checked}
value={el.value}
onChange={this.handleClick.bind(this,index)}
/>
}
key={el.value}
label={el.title}
/>
);
}

handleClick(index) {
let switches = this.state.switches;
switches[index].checked = !switches[index].checked;
this.setState({
switches,
}, () => {
jQuery.ajax({
type: 'POST',
url: `${this.apiEndpoint}/admin/setEpisode`,
xhrFields: { withCredentials: true },
data: {
guid: switches[index].guid,
enabled: switches[index].checked
}
});
this.forceUpdate()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we do this without forceUpdate? Maybe use the POST response to update the component state? I've read not to use forceUpdate, but probably not a big deal here and I do like how simple this component is otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I was unable to figure out the React lifecycle stuff to make an update happen in a way that made sense here without just doing a forceUpdate. I'm okay with it for now because it's admin-facing, but generally I wouldn't do it if it were a listener-facing feature.

});
}

render() {
return(
<div>
<Paper>
<div className="hero-space">
<div className="hero-content">
<img src={logo} className="logo" alt={parentSiteName}/>
<h2 className="tagline">Admin Panel</h2>
</div>
</div>
<div className="content episodes">
<h3 className="recent-episodes">Enable/Disable Episodes</h3>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users can see this before authenticating. Maybe that's ok cuz why would they be on this page in the first place? But we could wait until the withCredentials request above succeeds, then set component state to authenticated and render the text that says "Admin Panel" etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing exactly this, good call.

<FormGroup>
{this.renderSwitches.call(this)}
</FormGroup>
</div>
</Paper>
</div>
);
}
}

export default AdminComponent;
17 changes: 14 additions & 3 deletions client/src/components/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const Store = require('store'); // localStorage
const isSecure = window.location.protocol == 'https:';
const apiEndpoint_default = isSecure ? require('config').default.apiEndpointSsl : require('config').default.apiEndpoint;
const dataBucket = require('config').default.dataBucket;
const useRSS = require('config').default.useRSS;
const maxClipSeconds = require('config').default.maxClipSeconds;
const minClipSeconds = require('config').default.minClipSeconds;
const env = require('config').default.appEnv;
Expand Down Expand Up @@ -41,6 +42,7 @@ import NavBar from 'components/NavBarComponent';
import Loader from 'components/LoadingAnimationComponent';
import ShareContainer from 'components/ShareContainerComponent';
import Landing from 'components/Landing';
import Admin from 'components/Admin';
import Helpers from '../helpers';

/** The root React component */
Expand All @@ -52,6 +54,7 @@ class AppComponent extends React.Component {
constructor(props) {
super(props);
let localState, defaultState, userState;
let apiEndpoint = apiEndpoint_default;

// Required for the Material UI theme
injectTapEventPlugin();
Expand Down Expand Up @@ -105,11 +108,12 @@ class AppComponent extends React.Component {

// get episode data rendered by server
this.state.eps = window.__latestEpisodes || props.eps;
// BUT, if we're in a development environment, just grab the JSON file of all episodes and overwrite
// BUT, if we're in a development environment, just grab the JSON/RSS file of all episodes and overwrite
if (env === 'dev') {
let devUrl = useRSS ? apiEndpoint + '/rss' : apiEndpoint + '/recent';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the /rss and /recent endpoints return the same result. I'm probably missing something, but why do we need to be able to access both from the client (and are we only hitting the /rss endpoint in dev)? It'd be nice if we could get rid of this useRSS flag on the client so that only the server needs to know the datasource.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I had originally envisioned the /rss endpoint to be a simple local mirror of the RSS feed to get around CORS issues, but I think it makes sense to just keep /recent and have it render the rss data if that's enabled on the server. And then no useRSS flag!

Re: only hitting that endpoint in dev, that's technically true but what is really happening is in production, we render the front page using the marko template, which loads the episodes using the allEpisodeData function, which in turn checks for RSS and preloads those episodes, or falls back to the old explicit json method.

jQuery.ajax({
method: 'GET',
url: dataBucket + 'episodes.json',
url: devUrl,
crossDomain : true,
headers: {'X-Requested-With': 'XMLHttpRequest'},
success: (data) => {
Expand All @@ -123,7 +127,7 @@ class AppComponent extends React.Component {
});
window.console.error(this.props.url, status, err.toString());
}.bind(this)
})
});
}

this.state.episodesWithProblems = window.__inactiveEpisodes || require('config').default.episodesWithProblems;
Expand Down Expand Up @@ -832,6 +836,13 @@ class AppComponent extends React.Component {
let content;

switch(this.state.view) {
case 'admin':
content =
<Admin
eps={this.state.eps}
apiEndpoint={apiEndpoint_default}
/>;
break;
case 'about':
content =
<div className="content">
Expand Down
5 changes: 4 additions & 1 deletion client/src/config/base.js.template
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ export default {
// Uncomment this and add your cloudFront domain to enable Amazon Cloudfront CDN,
// make sure to have CORS headers whitelisted!
// cloudFrontDomain: 'dxxxxxxxxxx.cloudfront.net',
episodesWithProblems: []
episodesWithProblems: [],
// set `useRSS` to `true` if you plan to use the RSS_FEED defined in server/.env as your
// source for episode metadata (recommended)
useRSS: false
}
Empty file added client/src/styles/Admin.scss
Empty file.
6 changes: 6 additions & 0 deletions server/.env-template
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ CLOUDFRONT_URL=https://<ID>.cloudfront.net/
# Array of bad episodes separated by commas, i.e. [443, 300]
BAD_EPISODES=[]
DATA_BUCKET=http://localhost:8080/

# RSS feed URL (leave blank to use episodes.json stored in DATA_BUCKET instead)
RSS_FEED=

# Password for the admin panel
ADMIN_PASSWORD=1234
1 change: 1 addition & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ node_modules
_notes

public/
adminData.json
9 changes: 4 additions & 5 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Please refer to [the wiki](https://github.com/FeelTrainCoop/shortcut/wiki) instead. This README will soon be updated to reflect the current state of the project.

# tal-server
# shortcut-server
A node/express server that
- serves a single page application ("tal-client")
- serves a single page application ("shortcut-client")
- provides an API for handling social authentication for the client. Currently Twitter and Facebook are supported.
- provides web hooks for updating individual episodes and the list of available episodes
- ensures that only authorized requests can access episode data
Expand All @@ -16,7 +16,6 @@ A node/express server that
##### Install dependencies
```
npm install
npm install -g nodemon
```

##### Env Variables
Expand All @@ -28,8 +27,8 @@ Copy `.env-template` to `.env` and fill in environment variables.

### Build & run with Docker:
- Mac only: eval "$(docker-machine env default)"
- `docker build -t tal-server .`
- `docker run -p 3000:3000 -d tal-server`
- `docker build -t shortcut-server .`
- `docker run -p 3000:3000 -d shortcut-server`
- server will be running at docker-machine ip, i.e. `http://192.168.99.100:3000/`

### Run with Node:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some of the es6 node requires 6.x or higher? It says 4.4.3 down here so we should update and maybe add engines to package.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I deleted the line in question to avoid confusion

Expand Down
Loading