diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..1edad4b653470d853ca94ae187fea53d2eb37494 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/backend/public/algorithms"] + path = src/backend/public/algorithms + url = git@github.com:algorithm-visualizer/algorithms.git diff --git a/environment.js b/environment.js index c9c9819cef88df30696cf1a91a876bb1fc629916..fc365b163a2b434153c27125b7deb8c06f9d14e5 100644 --- a/environment.js +++ b/environment.js @@ -8,6 +8,10 @@ const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, + GITHUB_BOT_USERNAME, + GITHUB_BOT_PASSWORD, + GITHUB_ORG = 'algorithm-visualizer', + GITHUB_REPO_ALGORITHMS = 'algorithms', } = process.env; const __PROD__ = NODE_ENV === 'production'; @@ -18,6 +22,14 @@ const proxyPort = parseInt(PROXY_PORT); const githubClientId = GITHUB_CLIENT_ID; const githubClientSecret = GITHUB_CLIENT_SECRET; +const githubBotAuth = { + username: GITHUB_BOT_USERNAME, + password: GITHUB_BOT_PASSWORD, +}; +const githubOrg = GITHUB_ORG; +const githubRepos = { + algorithms: GITHUB_REPO_ALGORITHMS +}; const builtPath = path.resolve(__dirname, 'built'); const frontendBuiltPath = path.resolve(builtPath, 'frontend'); @@ -36,6 +48,9 @@ module.exports = { proxyPort, githubClientId, githubClientSecret, + githubBotAuth, + githubOrg, + githubRepos, frontendBuiltPath, backendBuiltPath, frontendSrcPath, diff --git a/src/backend/controllers/auth.js b/src/backend/controllers/auth.js index e3d43f3407edcfb2460333cb1f215846aeec6cbe..6855ae429f8b844af4fca0834a6952573cb0eb36 100644 --- a/src/backend/controllers/auth.js +++ b/src/backend/controllers/auth.js @@ -21,7 +21,7 @@ const response = (req, res, next) => { const { access_token } = response.data; res.cookie('access_token', access_token); res.redirect('/'); - }); + }).catch(next); }; const destroy = (req, res, next) => { diff --git a/src/backend/controllers/directory.js b/src/backend/controllers/hierarchy.js similarity index 73% rename from src/backend/controllers/directory.js rename to src/backend/controllers/hierarchy.js index be4ed502672ec628aa7017d8717326b55c2fa2fb..f68883e812f22105801dead939d28934756b01a6 100644 --- a/src/backend/controllers/directory.js +++ b/src/backend/controllers/hierarchy.js @@ -5,11 +5,11 @@ import { NotFoundError } from '/common/error'; const router = express.Router(); -const getPath = (...args) => path.resolve(__dirname, '..', '..', '..', 'algorithm', ...args); +const getPath = (...args) => path.resolve(__dirname, '..', 'public', 'algorithms', ...args); +const createKey = name => name.toLowerCase().replace(/ /g, '-'); +const list = dirPath => fs.readdirSync(dirPath).filter(filename => !filename.startsWith('.')); -const readCategories = () => { - const createKey = name => name.toLowerCase().replace(/ /g, '-'); - const list = dirPath => fs.readdirSync(dirPath).filter(filename => !filename.startsWith('.')); +const cacheHierarchy = () => { const getCategory = categoryName => { const categoryKey = createKey(categoryName); const categoryPath = getPath(categoryName); @@ -33,16 +33,16 @@ const readCategories = () => { return list(getPath()).map(getCategory); }; -const categories = readCategories(); +const hierarchy = cacheHierarchy(); -const getCategories = (req, res, next) => { - res.json({ categories }); +const getHierarchy = (req, res, next) => { + res.json({ hierarchy }); }; const getFile = (req, res, next) => { const { categoryKey, algorithmKey, fileName } = req.params; - const category = categories.find(category => category.key === categoryKey); + const category = hierarchy.find(category => category.key === categoryKey); if (!category) return next(new NotFoundError()); const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey); if (!algorithm) return next(new NotFoundError()); @@ -53,7 +53,7 @@ const getFile = (req, res, next) => { }; router.route('/') - .get(getCategories); + .get(getHierarchy); router.route('/:categoryKey/:algorithmKey/:fileName') .get(getFile); diff --git a/src/backend/controllers/index.js b/src/backend/controllers/index.js index 9c9e1a1a33a9bf4d1080ba0d512cc2f3c60b1cb2..3f9484e72cae0d997be393b0410dc3ac0b0fd2c7 100644 --- a/src/backend/controllers/index.js +++ b/src/backend/controllers/index.js @@ -1,13 +1,13 @@ import express from 'express'; import { AuthorizationError, NotFoundError, PermissionError } from '/common/error'; import auth from './auth'; -import directory from './directory'; +import hierarchy from './hierarchy'; import wiki from './wiki'; const router = new express.Router(); router.use('/auth', auth); -router.use('/directory', directory); +router.use('/hierarchy', hierarchy); router.use('/wiki', wiki); router.use((req, res, next) => next(new NotFoundError())); router.use((err, req, res, next) => { diff --git a/src/backend/public/algorithms b/src/backend/public/algorithms new file mode 160000 index 0000000000000000000000000000000000000000..417e8c9ffe9bf8411f0099ea01098c0919468a07 --- /dev/null +++ b/src/backend/public/algorithms @@ -0,0 +1 @@ +Subproject commit 417e8c9ffe9bf8411f0099ea01098c0919468a07 diff --git a/src/frontend/apis/index.js b/src/frontend/apis/index.js index f0c333cb0b5e207f61a180778b6cd7adf72684ef..7a3d67b54b69e3d4de760ef2c8d0a6a44be9b2ff 100644 --- a/src/frontend/apis/index.js +++ b/src/frontend/apis/index.js @@ -2,7 +2,7 @@ import Promise from 'bluebird'; import axios from 'axios'; import GitHub from 'github-api'; -let gitHub = new GitHub(); +let gh = new GitHub(); axios.interceptors.response.use(response => { return response.data; @@ -49,9 +49,9 @@ const PUT = URL => { }); }; -const DirectoryApi = { - getCategories: GET('/directory'), - getFile: GET('/directory/:categoryKey/:algorithmKey/:fileName'), +const HierarchyApi = { + getHierarchy: GET('/hierarchy'), + getFile: GET('/hierarchy/:categoryKey/:algorithmKey/:fileName'), }; const WikiApi = { @@ -60,12 +60,12 @@ const WikiApi = { }; const GitHubApi = { - auth: token => gitHub = new GitHub({ token }), - getProfile: () => gitHub.getUser().getProfile(), + auth: token => gh = new GitHub({ token }), + getProfile: () => gh.getUser().getProfile(), }; export { - DirectoryApi, + HierarchyApi, WikiApi, GitHubApi, }; \ No newline at end of file diff --git a/src/frontend/components/App/index.jsx b/src/frontend/components/App/index.jsx index 5c9b2c557d0db9181a3e5470021f2938b0b35f29..493f6d8cc38dd36c1568a6e1124a7f7d804e3b71 100644 --- a/src/frontend/components/App/index.jsx +++ b/src/frontend/components/App/index.jsx @@ -6,7 +6,7 @@ import { Workspace, WSSectionContainer, WSTabContainer } from '/workspace/compon import { Section } from '/workspace/core'; import { actions as toastActions } from '/reducers/toast'; import { actions as envActions } from '/reducers/env'; -import { DirectoryApi, GitHubApi } from '/apis'; +import { HierarchyApi, GitHubApi } from '/apis'; import { tracerManager } from '/core'; import styles from './stylesheet.scss'; import 'axios-progress-bar/dist/nprogress.css' @@ -33,11 +33,11 @@ class App extends React.Component { componentDidMount() { this.updateDirectory(this.props.match.params); - DirectoryApi.getCategories() - .then(({ categories }) => { - this.props.setCategories(categories); + HierarchyApi.getHierarchy() + .then(({ hierarchy }) => { + this.props.setHierarchy(hierarchy); const { categoryKey, algorithmKey } = this.props.env; - const category = categories.find(category => category.key === categoryKey) || categories[0]; + const category = hierarchy.find(category => category.key === categoryKey) || hierarchy[0]; const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey) || category.algorithms[0]; this.props.history.push(`/${category.key}/${algorithm.key}`); }); @@ -63,7 +63,7 @@ class App extends React.Component { updateDirectory({ categoryKey = null, algorithmKey = null }) { if (categoryKey && algorithmKey) { this.props.setDirectory(categoryKey, algorithmKey); - DirectoryApi.getFile(categoryKey, algorithmKey, 'code.js').then(code => tracerManager.setCode(code)); + HierarchyApi.getFile(categoryKey, algorithmKey, 'code.js').then(code => tracerManager.setCode(code)); } } @@ -88,11 +88,11 @@ class App extends React.Component { } render() { - const { categories, categoryKey, algorithmKey } = this.props.env; + const { hierarchy, categoryKey, algorithmKey } = this.props.env; const navigatorOpened = true; - return categories && categoryKey && algorithmKey && ( + return hierarchy && categoryKey && algorithmKey && (
this.setState({ source })) .catch(() => this.setState({ source: null })); } diff --git a/src/frontend/components/Header/index.jsx b/src/frontend/components/Header/index.jsx index b7680c4b695ff328f6ca04ebfec52ec4dc94fbb3..93ee1c5f41646f3efe2885bb00b1b6fad748663e 100644 --- a/src/frontend/components/Header/index.jsx +++ b/src/frontend/components/Header/index.jsx @@ -70,9 +70,9 @@ class Header extends React.Component { render() { const { interval, paused, started, profile } = this.state; const { className, onClickTitleBar, navigatorOpened } = this.props; - const { categories, categoryKey, algorithmKey, signedIn } = this.props.env; + const { hierarchy, categoryKey, algorithmKey, signedIn } = this.props.env; - const category = categories.find(category => category.key === categoryKey); + const category = hierarchy.find(category => category.key === categoryKey); const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey); return ( diff --git a/src/frontend/components/Navigator/index.jsx b/src/frontend/components/Navigator/index.jsx index 37d03dd98739ca03f2eab5b71f7c7fe7a9465235..d03fdd4956208fba16a69accd1793d39ef24a1d6 100644 --- a/src/frontend/components/Navigator/index.jsx +++ b/src/frontend/components/Navigator/index.jsx @@ -40,10 +40,10 @@ class Navigator extends React.Component { } handleChangeQuery(e) { - const { categories } = this.props.env; + const { hierarchy } = this.props.env; const categoriesOpened = {}; const query = e.target.value; - categories.forEach(category => { + hierarchy.forEach(category => { if (this.testQuery(name) || category.algorithms.find(algorithm => this.testQuery(algorithm.name))) { categoriesOpened[category.key] = true; } @@ -60,7 +60,7 @@ class Navigator extends React.Component { render() { const { categoriesOpened, query } = this.state; const { className, style } = this.props; - const { categoryKey: selectedCategoryKey, algorithmKey: selectedAlgorithmKey, categories } = this.props.env; + const { hierarchy, categoryKey: selectedCategoryKey, algorithmKey: selectedAlgorithmKey } = this.props.env; return (
{ - categories.map(category => { + hierarchy.map(category => { const categoryOpened = categoriesOpened[category.key]; let algorithms = category.algorithms; if (!this.testQuery(category.name)) { diff --git a/src/frontend/reducers/env.js b/src/frontend/reducers/env.js index b875a11245669df4e946db58716ab63851ccf58d..d6dbb0c758b5bd315c43d07e2f6046ff96e252ab 100644 --- a/src/frontend/reducers/env.js +++ b/src/frontend/reducers/env.js @@ -3,20 +3,20 @@ import { combineActions, createAction, handleActions } from 'redux-actions'; const prefix = 'ENV'; -const setCategories = createAction(`${prefix}/SET_CATEGORIES`, categories => ({ categories })); +const setHierarchy = createAction(`${prefix}/SET_HIERARCHY`, hierarchy => ({ hierarchy })); const setDirectory = createAction(`${prefix}/SET_DIRECTORY`, (categoryKey, algorithmKey) => ({ categoryKey, algorithmKey, })); export const actions = { - setCategories, + setHierarchy, setDirectory, }; const accessToken = Cookies.get('access_token'); const defaultState = { - categories: null, + hierarchy: null, categoryKey: null, algorithmKey: null, accessToken, @@ -25,7 +25,7 @@ const defaultState = { export default handleActions({ [combineActions( - setCategories, + setHierarchy, setDirectory, )]: (state, { payload }) => ({ ...state,