diff --git a/README.md b/README.md index 3e3d5131..2d6e507f 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,19 @@ OpenSupports v4.0 8. Run `gulp dev` 9. Go to the main app: `http://localhost:3000/app` or the component demo `http://localhost:3000/demo` 10. Your browser will automatically be opened and directed to the browser-sync proxy address +12. Use `gulp dev --api` to disable fixtures and use the real php server api (it must be running at :8080). Now that `gulp dev` is running, the server is up as well and serving files from the `/build` directory. Any changes in the `/src` directory will be automatically processed by Gulp and the changes will be injected to any open browsers pointed at the proxy address. ### Getting up and running BACK-END 1. Clone this repo -2. [Create MySQL Database](#markdown-header-create-mysql-database) -TODO +2. [Install PHP5](http://www.howtogeek.com/howto/ubuntu/installing-php5-and-apache-on-ubuntu/) +3. [Create MySQL Database](#markdown-header-create-mysql-database) +4. [Install composer](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-composer-on-ubuntu-14-04) +5. Go to `cd os4-react/api` +6. Run `composer install` +7. Run the server with `php -S localhost:8080` ### Create MySQL Database diff --git a/api/controllers/user.php b/api/controllers/user.php index 13eb400d..5aa9cfeb 100644 --- a/api/controllers/user.php +++ b/api/controllers/user.php @@ -1,51 +1,11 @@ group('/user', function () use ($app) { +include 'user/login.php'; +include 'user/signup.php'; - $app->get('/get/(:by)/(:value)', function () use ($app) { - echo "Returns the user with $by = $value as a json"; - }); +$userControllers = new ControllerGroup(); +$userControllers->setGroupPath('/user'); - $app->post('/create', function () use ($app) { - $email = Controller::request('email'); - $password = Controller::request('password'); +$userControllers->addController(new LoginController); +$userControllers->addController(new SignUpController); - $userInstance = new User(); - $userInstance->setProperties(array( - 'email' => $email, - 'password' => User::hashPassword($password), - 'admin' => 0 - )); - $userId = $userInstance->store(); - - Response::respondSuccess(array( - 'userId' => $userId, - 'userEmail' => $email - )); - }); - - $app->post('/login', function () use ($app) { - $session = Session::getInstance(); - - $email = Controller::request('email'); - $password = Controller::request('password'); - - if ($session->sessionExists()) { - Response::respondError(ERRORS::SESSION_EXISTS); - } - - $userInstance = User::authenticate($email, $password); - - if (!$userInstance) { - Response::respondError(ERRORS::INVALID_CREDENTIALS); - } - - $session->createSession($userInstance->id); - - Response::respondSuccess(array( - 'userId' => $userInstance->id, - 'userEmail' => $userInstance->email, - 'userIsAdmin' => $userInstance->admin, - 'token' => $session->getToken() - )); - }); -}); \ No newline at end of file +$userControllers->finalize(); diff --git a/api/controllers/user/login.php b/api/controllers/user/login.php new file mode 100644 index 00000000..d784b5d2 --- /dev/null +++ b/api/controllers/user/login.php @@ -0,0 +1,32 @@ +sessionExists()) { + Response::respondError(ERRORS::SESSION_EXISTS); + return; + } + + $userInstance = User::getUser($email, $password); + + if ($userInstance !== null) { + $session->createSession($userInstance->id); + + Response::respondSuccess(array( + 'userId' => $userInstance->id, + 'userEmail' => $userInstance->email, + 'userIsAdmin' => $userInstance->admin, + 'token' => $session->getToken() + )); + } else { + Response::respondError(ERRORS::INVALID_CREDENTIALS); + } + } +} diff --git a/api/controllers/user/signup.php b/api/controllers/user/signup.php new file mode 100644 index 00000000..4fd2c48c --- /dev/null +++ b/api/controllers/user/signup.php @@ -0,0 +1,28 @@ +createNewUserAndRetrieveId($email, $password); + + Response::respondSuccess(array( + 'userId' => $userId, + 'userEmail' => $email + )); + } + + public function createNewUserAndRetrieveId() { + $userInstance = new User(); + $userInstance->setProperties(array( + 'email' => $email, + 'password' => User::hashPassword($password), + 'admin' => 0 + )); + + return $userInstance->store(); + } +} diff --git a/api/index.php b/api/index.php index 39e84480..ba5ea782 100644 --- a/api/index.php +++ b/api/index.php @@ -20,7 +20,9 @@ spl_autoload_register(function ($class) { }); // LOAD CONTROLLERS -include 'libs/Controller.php'; +include_once 'libs/Controller.php'; +include_once 'libs/ControllerGroup.php'; + foreach (glob('controllers/*.php') as $controller) { include $controller; } diff --git a/api/libs/Controller.php b/api/libs/Controller.php index ab6834c7..11770fc7 100644 --- a/api/libs/Controller.php +++ b/api/libs/Controller.php @@ -1,8 +1,20 @@ handler(); + }; + } + public static function request($key) { - $app = \Slim\Slim::getInstance(); + $app = self::getAppInstance(); return $app->request()->post($key); } @@ -27,4 +39,8 @@ class Controller { public static function checkAdminLogged() { return self::checkUserLogged() && (self::getLoggedUser()->admin === 2); } + + public static function getAppInstance() { + return \Slim\Slim::getInstance(); + } } \ No newline at end of file diff --git a/api/libs/ControllerGroup.php b/api/libs/ControllerGroup.php new file mode 100644 index 00000000..b91c8a72 --- /dev/null +++ b/api/libs/ControllerGroup.php @@ -0,0 +1,24 @@ +groupPath = $groupPath; + } + + public function addController($controller) { + array_push($this->controllers, $controller); + } + + public function finalize() { + $app = Controller::getAppInstance(); + $controllers = $this->controllers; + + $app->group($this->groupPath, function () use ($app, $controllers) { + foreach ($controllers as $controller) { + $app->post($controller::PATH, $controller->getHandler()); + } + }); + } +} diff --git a/gulp/tasks/browserSync.js b/gulp/tasks/browserSync.js index 69046d3e..5a2ed387 100644 --- a/gulp/tasks/browserSync.js +++ b/gulp/tasks/browserSync.js @@ -7,7 +7,8 @@ var gulp = require('gulp'); gulp.task('browserSync', function() { browserSync({ - proxy: 'localhost:' + config.serverport + proxy: 'localhost:' + config.serverport, + startPath: 'app' }); }); \ No newline at end of file diff --git a/gulp/tasks/browserify.js b/gulp/tasks/browserify.js index e28b8f93..949f0761 100644 --- a/gulp/tasks/browserify.js +++ b/gulp/tasks/browserify.js @@ -15,6 +15,7 @@ var browserSync = require('browser-sync'); var debowerify = require('debowerify'); var handleErrors = require('../util/handle-errors'); var config = require('../config'); +var util = require('gulp-util'); // Based on: http://blog.avisi.nl/2014/04/25/how-to-keep-a-fast-build-with-browserify-and-reactjs/ function buildScript(file, watch) { @@ -22,6 +23,11 @@ function buildScript(file, watch) { var bundler = browserify({ entries: [config.sourceDir + 'app/' + file], debug: !global.isProd, + insertGlobalVars: { + noFixtures: function() { + return (util.env['api']) ? "'enabled'" : "'disabled'"; + } + }, cache: {}, packageCache: {}, fullPaths: true diff --git a/package.json b/package.json index 96a8b771..97366c3e 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "gulp-util": "^3.0.6", "humps": "^0.6.0", "jest-cli": "^0.5.10", + "jquery-mockjax": "^2.1.0", "morgan": "^1.6.1", "run-sequence": "^1.1.1", "vinyl-source-stream": "^1.1.0", @@ -56,7 +57,8 @@ "react-google-recaptcha": "^0.5.2", "react-motion": "^0.3.0", "react-router": "^2.0.0-rc5", - "reflux": "^0.2.9" + "reflux": "^0.2.9", + "sessionstorage": "0.0.1" }, "jest": { "scriptPreprocessor": "./preprocessor.js", diff --git a/src/app/index.js b/src/app/index.js index a3ed6c9c..7a01f3da 100644 --- a/src/app/index.js +++ b/src/app/index.js @@ -5,8 +5,12 @@ import Router from 'react-router'; import routes from './Routes'; if ( process.env.NODE_ENV !== 'production' ) { - // Enable React devtools - window.React = React; + // Enable React devtools + window.React = React; +} + +if (noFixtures === 'disabled') { + require('lib-app/fixtures-loader'); } render(routes, document.getElementById('app')); diff --git a/src/app/main/main-layout-header.js b/src/app/main/main-layout-header.js index c1be8026..ba54472b 100644 --- a/src/app/main/main-layout-header.js +++ b/src/app/main/main-layout-header.js @@ -1,6 +1,6 @@ import React from 'react'; -import i18n from 'lib/i18n'; +import i18n from 'lib-app/i18n'; import CommonActions from 'actions/common-actions'; import Button from 'core-components/button'; diff --git a/src/core-components/button.js b/src/core-components/button.js index 1884cf6b..0322eab7 100644 --- a/src/core-components/button.js +++ b/src/core-components/button.js @@ -1,7 +1,7 @@ import React from 'react'; import classNames from 'classnames'; import Router from 'react-router'; -import callback from 'lib/callback'; +import callback from 'lib-core/callback'; let Button = React.createClass({ diff --git a/src/core-components/checkbox.js b/src/core-components/checkbox.js index 1bfff8c4..48f775a2 100644 --- a/src/core-components/checkbox.js +++ b/src/core-components/checkbox.js @@ -2,8 +2,8 @@ import React from 'react'; import classNames from 'classnames'; import _ from 'lodash'; -import callback from 'lib/callback'; -import getIcon from 'lib/get-icon'; +import callback from 'lib-core/callback'; +import getIcon from 'lib-core/get-icon'; let CheckBox = React.createClass({ diff --git a/src/core-components/drop-down.js b/src/core-components/drop-down.js index db276b1f..53f63c57 100644 --- a/src/core-components/drop-down.js +++ b/src/core-components/drop-down.js @@ -3,7 +3,7 @@ import classNames from 'classnames'; import _ from 'lodash'; import {Motion, spring} from 'react-motion'; -import callback from 'lib/callback'; +import callback from 'lib-core/callback'; let DropDown = React.createClass({ diff --git a/src/core-components/form.js b/src/core-components/form.js index c1b65fe0..a407602e 100644 --- a/src/core-components/form.js +++ b/src/core-components/form.js @@ -1,7 +1,7 @@ import React from 'react'; import _ from 'lodash'; -import {reactDFS, renderChildrenWithProps} from 'lib/react-dfs'; +import {reactDFS, renderChildrenWithProps} from 'lib-core/react-dfs'; import Input from 'core-components/input'; import Checkbox from 'core-components/checkbox'; diff --git a/src/data/fixtures/user-fixtures.js b/src/data/fixtures/user-fixtures.js new file mode 100644 index 00000000..cba6af7e --- /dev/null +++ b/src/data/fixtures/user-fixtures.js @@ -0,0 +1,26 @@ +module.exports = [ + { + path: 'user/login', + time: 1000, + response: function (data) { + let response; + + if (data.password === 'invalid') { + response = { + status: 'fail', + message: 'Invalid Credientals' + }; + } else { + response = { + status: 'success', + data: { + 'userId': 12, + 'token': 'cc6b4921e6733d6aafe284ec0d7be57e' + } + }; + } + + return response; + } + } +]; diff --git a/src/lib-app/api-call.js b/src/lib-app/api-call.js new file mode 100644 index 00000000..a2885b08 --- /dev/null +++ b/src/lib-app/api-call.js @@ -0,0 +1,22 @@ +const _ = require('lodash'); +const APIUtils = require('lib-core/APIUtils'); +const SessionStorage = require('sessionstorage'); + +const root = 'http://localhost:3000/api/'; + +function processData (data) { + return _.extend({ + userId: SessionStorage.getItem('userId'), + token: SessionStorage.getItem('token') + }, data); +} + +module.exports = { + call: function (path, data, callback) { + APIUtils.post(root + path, processData(data)).then(callback); + }, + setConfig: function (userId, token) { + SessionStorage.setItem('userId', userId); + SessionStorage.setItem('token', token); + } +}; \ No newline at end of file diff --git a/src/lib-app/fixtures-loader.js b/src/lib-app/fixtures-loader.js new file mode 100644 index 00000000..fe8b9fc3 --- /dev/null +++ b/src/lib-app/fixtures-loader.js @@ -0,0 +1,30 @@ +const _ = require('lodash'); +const $ = require('jquery'); +const mockjax = require('jquery-mockjax')($, window); + +let fixtures = (function () { + let fixturesData = []; + + return { + add(fixtures) { + fixturesData = fixturesData.concat(fixtures); + }, + getAll() { + return fixturesData; + } + }; +})(); + +// FIXTURES +fixtures.add(require('data/fixtures/user-fixtures')); + +_.each(fixtures.getAll(), function (fixture) { + mockjax({ + contentType: 'application/json', + url: 'http://localhost:3000/api/' + fixture.path, + responseTime: fixture.time || 500, + response: function (settings) { + this.responseText = fixture.response(settings.data); + } + }); +}); diff --git a/src/lib/i18n.js b/src/lib-app/i18n.js similarity index 100% rename from src/lib/i18n.js rename to src/lib-app/i18n.js diff --git a/src/lib/APIUtils.js b/src/lib-core/APIUtils.js similarity index 86% rename from src/lib/APIUtils.js rename to src/lib-core/APIUtils.js index d8d5350e..2ba05eea 100644 --- a/src/lib/APIUtils.js +++ b/src/lib-core/APIUtils.js @@ -1,15 +1,12 @@ -'use strict'; - -import $ from 'jquery'; +const _ = require('lodash'); +const $ = require('jquery'); const APIUtils = { - root: 'http://localhost:3000/api/', - getPromise(path, method, data) { return (resolve, reject) => { $.ajax({ - url: this.root + path, + url: path, method: method, data: data, dataType: 'json' diff --git a/src/lib/callback.js b/src/lib-core/callback.js similarity index 100% rename from src/lib/callback.js rename to src/lib-core/callback.js diff --git a/src/lib/get-icon.js b/src/lib-core/get-icon.js similarity index 100% rename from src/lib/get-icon.js rename to src/lib-core/get-icon.js diff --git a/src/lib/react-dfs.js b/src/lib-core/react-dfs.js similarity index 100% rename from src/lib/react-dfs.js rename to src/lib-core/react-dfs.js diff --git a/src/lib/route-transition.js b/src/lib-core/route-transition.js similarity index 100% rename from src/lib/route-transition.js rename to src/lib-core/route-transition.js diff --git a/src/stores/user-store.js b/src/stores/user-store.js index c5f3227c..200c2ed7 100644 --- a/src/stores/user-store.js +++ b/src/stores/user-store.js @@ -1,24 +1,26 @@ -import Reflux from 'reflux'; -import APIUtils from 'lib/APIUtils'; +const Reflux = require('reflux'); +const API = require('lib-app/api-call'); -import UserActions from 'actions/user-actions'; +const UserActions = require('actions/user-actions'); -let UserStore = Reflux.createStore({ +const UserStore = Reflux.createStore({ - init() { - this.user = null; - this.hasBeenChecked = false; + init() { + this.user = null; + this.hasBeenChecked = false; - this.listenTo(UserActions.checkLoginStatus, this.checkLoginStatus); - this.listenTo(UserActions.login, this.loginUser); - this.listenTo(UserActions.logout, this.logoutUser); - }, + this.listenTo(UserActions.checkLoginStatus, this.checkLoginStatus); + this.listenTo(UserActions.login, this.loginUser); + this.listenTo(UserActions.logout, this.logoutUser); + }, loginUser(loginData) { - APIUtils.post('user/login', loginData).then(result => { + API.call('user/login', loginData, result => { console.log(result); + + API.setConfig(result.userId, result.token); }); - } + } }); export default UserStore; \ No newline at end of file