├── src
├── tmp
│ └── .gitignore
├── public
│ └── assets
│ │ └── img
│ │ ├── logo.png
│ │ ├── user-13.jpg
│ │ ├── login-bg
│ │ └── bg-7.jpg
│ │ ├── transparent
│ │ ├── black-0.1.png
│ │ ├── black-0.2.png
│ │ ├── black-0.3.png
│ │ ├── black-0.4.png
│ │ ├── black-0.5.png
│ │ ├── black-0.6.png
│ │ ├── black-0.7.png
│ │ ├── black-0.8.png
│ │ ├── black-0.9.png
│ │ ├── white-0.1.png
│ │ ├── white-0.2.png
│ │ ├── white-0.3.png
│ │ ├── white-0.4.png
│ │ ├── white-0.5.png
│ │ ├── white-0.6.png
│ │ ├── white-0.7.png
│ │ ├── white-0.8.png
│ │ ├── white-0.9.png
│ │ └── white-0.98.png
│ │ └── screenshots
│ │ ├── koa-2-dashboard.jpeg
│ │ └── koa-2-login-osx.jpeg
├── middleware
│ ├── index.js
│ └── checkauth.js
├── routes
│ ├── main.js
│ ├── open.js
│ ├── auth.js
│ ├── index.js
│ └── mock.js
├── app.js
├── config
│ ├── passport.js
│ ├── config.js
│ └── base.js
├── lib
│ └── db.js
├── models
│ └── account.js
└── views
│ └── main.swig
├── .babelrc
├── start.js
├── app
├── store
│ ├── history.js
│ └── configureStore.js
├── reducers
│ ├── index.js
│ ├── authed.js
│ └── main.js
├── index.js
├── constants
│ └── ActionTypes.js
├── components
│ ├── Github.js
│ ├── Exception.js
│ ├── Header.js
│ ├── Leftbar.js
│ ├── Summary.js
│ └── Trend.js
├── containers
│ ├── App.js
│ ├── DashboardContainer.js
│ └── LoginContainer.js
└── actions
│ ├── main.js
│ └── authed.js
├── Dockerfile
├── test
└── test.spec.js
├── webpack.prod.config.js
├── webpack.config.js
├── README.md
├── package.json
└── .gitignore
/src/tmp/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["stage-0", "es2015"]
3 | }
--------------------------------------------------------------------------------
/start.js:
--------------------------------------------------------------------------------
1 | require("babel-core/register");
2 | require("babel-polyfill");
3 |
4 | require("./src/app.js");
5 |
--------------------------------------------------------------------------------
/src/public/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/logo.png
--------------------------------------------------------------------------------
/src/public/assets/img/user-13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/user-13.jpg
--------------------------------------------------------------------------------
/src/public/assets/img/login-bg/bg-7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/login-bg/bg-7.jpg
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.1.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.2.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.3.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.4.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.5.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.6.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.7.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.8.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/black-0.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/black-0.9.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.1.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.2.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.3.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.4.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.5.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.6.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.7.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.8.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.9.png
--------------------------------------------------------------------------------
/src/public/assets/img/transparent/white-0.98.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/transparent/white-0.98.png
--------------------------------------------------------------------------------
/src/public/assets/img/screenshots/koa-2-dashboard.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/screenshots/koa-2-dashboard.jpeg
--------------------------------------------------------------------------------
/src/public/assets/img/screenshots/koa-2-login-osx.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/superalsrk/koa2-boilerplate/HEAD/src/public/assets/img/screenshots/koa-2-login-osx.jpeg
--------------------------------------------------------------------------------
/app/store/history.js:
--------------------------------------------------------------------------------
1 | import { useRouterHistory} from 'react-router';
2 | import { createHashHistory } from 'history';
3 |
4 | const appHistory = useRouterHistory(createHashHistory)({ queryKey: false })
5 |
6 | export default appHistory
--------------------------------------------------------------------------------
/app/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import authed from './authed';
3 | import main from './main';
4 |
5 |
6 | const rootReducer = combineReducers({
7 | authed,
8 | main
9 | })
10 |
11 | export default rootReducer
--------------------------------------------------------------------------------
/src/middleware/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import compose from 'koa-compose';
4 | import checkauth from './checkauth';
5 |
6 | export default function middleware() {
7 | return compose(
8 | [
9 | checkauth()
10 | ]
11 | )
12 | }
--------------------------------------------------------------------------------
/src/routes/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Router from 'koa-router';
4 |
5 | const router = new Router();
6 |
7 | router.get('/', async (ctx, next) => {
8 | ctx.body = {
9 | "status" : "home"
10 | }
11 | })
12 |
13 | router.get('/app', async (ctx, next) => {
14 | ctx.body = {
15 | "status" : "app"
16 | }
17 | })
18 |
19 |
20 | export default router;
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:6.3.0
2 |
3 | MAINTAINER superalsrk "https://github.com/superalsrk"
4 |
5 | RUN mkdir -p /var/app
6 |
7 | WORKDIR /var/app
8 |
9 | COPY . /var/app/
10 |
11 | RUN npm install
12 |
13 | RUN npm run build
14 |
15 | RUN npm install -g pm2
16 |
17 | ENV NODE_ENV production
18 |
19 | EXPOSE 5000
20 |
21 | # USER nobody
22 | WORKDIR /var/app
23 |
24 | CMD ["pm2", "start", "start.js", "--no-daemon"]
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { Provider } from 'react-redux';
4 |
5 |
6 | import configureStore from './store/configureStore'
7 | import App from './containers/App'
8 |
9 |
10 | const store = configureStore();
11 |
12 |
13 | render(
14 |
15 |
16 | ,
17 | document.getElementById('page-container')
18 | )
--------------------------------------------------------------------------------
/app/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const AUTH_SUCCESS = 'AUTH_SUCCESS'
2 | export const AUTH_FAILED = 'AUTH_FAILED'
3 |
4 | export const AUTH_STATUS_SUCCESS = 'AUTH_STATUS_SUCCESS'
5 | export const AUTH_STATUS_FAILED = 'AUTH_STATUS_FAIL'
6 |
7 | export const MAIN_RECEIVE_SUMMARY = 'MAIN_RECEIVE_SUMMARY'
8 | export const MAIN_RECEIVE_LINECHART = 'MAIN_RECEIVE_LINECHART'
9 | export const MAIN_LINECHART_DELETED = 'MAIN_LINECHART_DELETED'
--------------------------------------------------------------------------------
/app/reducers/authed.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery';
2 | import * as types from '../constants/ActionTypes';
3 |
4 |
5 | const auth = (state = [], action) => {
6 | switch(action.type) {
7 | case types.AUTH_SUCCESS:
8 | return state;
9 | case types.AUTH_FAILED:
10 | return state;
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | export default auth;
17 |
18 |
--------------------------------------------------------------------------------
/src/middleware/checkauth.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default function checkauth() {
4 | return async function (ctx, next) {
5 |
6 | if (ctx.isAuthenticated()
7 | || ctx.path.indexOf('/auth/') >= 0
8 | || ctx.path.indexOf('/open/') >= 0
9 | || ctx.path==='/'
10 | || ctx.path.indexOf('.html') >= 0) {
11 | await next()
12 | } else {
13 | ctx.body = {
14 | "status" : 401
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/components/Github.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import GitHubForkRibbon from 'react-github-fork-ribbon';
3 |
4 | class Github extends React.Component {
5 |
6 | render() {
7 | return (
8 |
12 | Fork me on GitHub
13 |
14 | )
15 | }
16 | }
17 |
18 | export default Github
19 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Koa from 'koa';
4 | import baseconfig from './config/base';
5 | import middleware from './middleware';
6 | import routes from './routes';
7 | import config from './config/config';
8 | import log4js from 'log4js';
9 |
10 | const app = new Koa();
11 | const LOG = log4js.getLogger('file')
12 |
13 |
14 | //configure basic app
15 | baseconfig(app)
16 |
17 | //configure custom middleware
18 | app.use(middleware())
19 |
20 | //configure custom routes
21 | app.use(routes())
22 |
23 | app.listen(config.app.port);
24 | LOG.info("Server started, listening on port: " + config.app.port);
25 |
26 | export default app
--------------------------------------------------------------------------------
/app/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunkMiddleware from 'redux-thunk';
3 | import rootReducer from '../reducers';
4 | import createLogger from 'redux-logger';
5 |
6 |
7 | const middlewares = [thunkMiddleware]
8 |
9 | console.log(process.env)
10 |
11 | if (process.env.NODE_ENV === `development`) {
12 |
13 | middlewares.push(createLogger());
14 | }
15 |
16 |
17 | const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
18 |
19 | export default function configureStore(preloadedState) {
20 | const store = createStoreWithMiddleware(rootReducer, preloadedState);
21 | return store
22 | }
--------------------------------------------------------------------------------
/src/routes/open.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Router from 'koa-router';
4 | import NodeExcel from 'excel-export';
5 |
6 | const router = new Router();
7 |
8 | router.get('/export', async (ctx, next) => {
9 | let conf = {}
10 | conf.cols = [{
11 | caption:'Header',
12 | type:'string',
13 | width: 145
14 | }]
15 |
16 | conf.rows = [['value01'], ['value02'], ['value03']]
17 |
18 | ctx.type = 'application/vnd.openxmlformats'
19 | ctx.attachment( "Report.xlsx")
20 | ctx.body = new Buffer(NodeExcel.execute(conf), 'binary')
21 | })
22 |
23 | router.get('/test', async(ctx, next) => {
24 | ctx.body = {
25 | 'status' : 'done'
26 | }
27 | })
28 |
29 | export default router;
--------------------------------------------------------------------------------
/src/config/passport.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import passport from 'koa-passport';
4 | import AccountModel from '../models/account';
5 |
6 | passport.serializeUser(function(user, done) {
7 | done(null, user.id)
8 | })
9 |
10 | passport.deserializeUser(function(id, done) {
11 | AccountModel.findOne(id, function(err, user) {
12 | done(err, user)
13 | })
14 | })
15 |
16 | var LocalStrategy = require('passport-local').Strategy
17 |
18 | passport.use(new LocalStrategy(function(username, password, done) {
19 |
20 | AccountModel.verify(username, password)
21 | .then(function(result) {
22 | if(result != null) {
23 | done(null, result)
24 | } else {
25 | done(null, false)
26 | }
27 | })
28 | }))
29 |
--------------------------------------------------------------------------------
/src/lib/db.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import mysql from 'mysql';
4 |
5 | const db = {}
6 |
7 | const mysqlPool = mysql.createPool({
8 | connectionLimit : 10,
9 | host : 'localhost',
10 | user : 'test',
11 | password : 'test',
12 | database : 'test'
13 | });
14 |
15 | db.pool = mysqlPool;
16 |
17 | db.query = async function(sql, value) {
18 | return new Promise((resolve, reject) => {
19 | mysqlPool.query(sql, value, function(err, results, fields) {
20 | if(err) {
21 | reject(Error(err))
22 | } else {
23 | resolve(results)
24 | }
25 | })
26 | })
27 | }
28 |
29 | db.queryAsync = function(sql, value, cb) {
30 | mysqlPool.query(sql, value, function(err, results, fields) {
31 | cb(err, results, fields)
32 | })
33 | }
34 |
35 | export default db;
36 |
--------------------------------------------------------------------------------
/app/containers/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import LoginContainer from '../containers/LoginContainer';
4 | import DashboardContainer from '../containers/DashboardContainer';
5 | import ExceptionComponent from '../components/Exception';
6 |
7 | import { Router, Route , Redirect} from 'react-router';
8 | import history from '../store/history';
9 |
10 | class App extends React.Component {
11 |
12 | componentDidMount() {
13 |
14 | }
15 |
16 | render() {
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | )
27 | }
28 | }
29 |
30 | export default App
--------------------------------------------------------------------------------
/src/routes/auth.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Router from 'koa-router';
4 | import passport from 'koa-passport';
5 |
6 | const router = new Router();
7 |
8 | router.get('/login', async (ctx, next) => {
9 | ctx.body = {
10 | "status" : "login page"
11 | }
12 | })
13 |
14 |
15 | router.post('/login', async (ctx, next) => {
16 | let middleware = passport.authenticate('local', async(user, info) => {
17 | if (user === false) {
18 | ctx.body = {
19 | 'status' : 400
20 | }
21 | } else {
22 | await ctx.login(user)
23 | ctx.body = {
24 | user: user
25 | }
26 | }
27 | })
28 | await middleware.call(this, ctx, next)
29 | })
30 |
31 | router.get('/logout', async (ctx, newt) => {
32 | ctx.logout()
33 | ctx.redirect('/')
34 | })
35 |
36 | router.get('/status', async (ctx, next) => {
37 | ctx.body = {
38 | "isLogin" : ctx.isAuthenticated()
39 | }
40 | })
41 |
42 | export default router;
43 |
--------------------------------------------------------------------------------
/app/components/Exception.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import GitHubForkRibbon from 'react-github-fork-ribbon';
3 |
4 | class Exception extends React.Component {
5 | componentDidMount() {
6 | document.body.classList.remove('bg-white')
7 | }
8 |
9 | render() {
10 | return (
11 |
12 |
13 |
404
14 |
15 |
We couldn't find it...
16 |
17 | The page you're looking for doesn't exist.
18 | Perhaps, there pages will help find what you're looking for.
19 |
20 |
23 |
24 |
25 | )
26 | }
27 | }
28 |
29 | export default Exception
30 |
--------------------------------------------------------------------------------
/src/routes/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import compose from 'koa-compose';
4 | import Router from 'koa-router';
5 |
6 |
7 | import RouterMain from './main';
8 | import RouterAuth from './auth';
9 | import RouterOpen from './open';
10 | import RouterMock from './mock';
11 |
12 | const router = new Router();
13 |
14 |
15 | router.get('/', async (ctx, next) => {
16 | // ctx.type = 'html'
17 | // ctx.body = require('fs').createReadStream(__dirname + '/../public/main.html')
18 |
19 | await ctx.render('./main')
20 | })
21 |
22 |
23 | router.use('/api', RouterMain.routes(), RouterMain.allowedMethods())
24 | router.use('/auth', RouterAuth.routes(), RouterAuth.allowedMethods())
25 | router.use('/open', RouterOpen.routes(), RouterOpen.allowedMethods())
26 | router.use('/mock', RouterMock.routes(), RouterMock.allowedMethods())
27 |
28 | router.get('*', async (ctx, next) => {
29 | ctx.body = { status : 404 }
30 | })
31 |
32 | export default function routes() {
33 | return compose(
34 | [
35 | router.routes(),
36 | router.allowedMethods()
37 | ]
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/app/components/Header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | class Header extends React.Component {
4 |
5 | render() {
6 | return (
7 |
27 | )
28 | }
29 | }
30 |
31 | export default Header
32 |
--------------------------------------------------------------------------------
/src/config/config.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const path = require("path");
3 | const _ = require("lodash");
4 |
5 | let env = process.env.NODE_ENV = process.env.NODE_ENV || "development";
6 |
7 | let base = {
8 | app: {
9 | root: path.normalize(path.join(__dirname, "/..")),
10 | env: env,
11 | },
12 | };
13 |
14 | let specific = {
15 | development: {
16 | app: {
17 | port: 5000,
18 | name: "koa2-boilerplate - Dev",
19 | excluded : "excluded_path"
20 | },
21 | mysql: {
22 | host: 'localhost',
23 | port : 3306,
24 | user : 'test',
25 | password : 'test',
26 | database : 'test'
27 | }
28 | },
29 | production: {
30 | app: {
31 | port: process.env.PORT || 5000,
32 | name: "koa2-boilerplate",
33 | excluded : "excluded_path"
34 | },
35 | mysql: {
36 | host: 'localhost',
37 | port : 3306,
38 | user : 'test',
39 | password : 'test',
40 | database : 'test'
41 | }
42 | },
43 | };
44 |
45 | module.exports = _.merge(base, specific[env]);
--------------------------------------------------------------------------------
/app/actions/main.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes';
2 |
3 | import 'whatwg-fetch';
4 |
5 | export function fetchSummaryData() {
6 | return dispatch => {
7 | fetch('/mock/summary', {
8 | credentials: 'same-origin'
9 | })
10 | .then( tresponse => {
11 | return tresponse.json()
12 | })
13 | .then( tjson => {
14 |
15 | dispatch({
16 | type : types.MAIN_RECEIVE_SUMMARY,
17 | data : tjson.data
18 | })
19 | })
20 | }
21 | }
22 |
23 | export function fetchMainChartData() {
24 | return dispatch => {
25 | fetch('/mock/linechart', {
26 | credentials: 'same-origin'
27 | })
28 | .then( tresponse => {
29 | return tresponse.json()
30 | })
31 | .then( tjson => {
32 |
33 | dispatch({
34 | type : types.MAIN_RECEIVE_LINECHART,
35 | data : tjson.data
36 | })
37 | })
38 | }
39 | }
40 |
41 | export function deleteLineChart() {
42 | return {
43 | type : types.MAIN_LINECHART_DELETED,
44 | }
45 | }
--------------------------------------------------------------------------------
/src/config/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import compose from 'koa-compose';
4 | import convert from 'koa-convert';
5 | import cors from 'kcors';
6 | import Serve from 'koa-static';
7 | import Logger from 'koa-logger';
8 | import mount from 'koa-mount';
9 | import bodyParser from 'koa-bodyparser';
10 | import session from 'koa-generic-session';
11 | import views from 'koa-views';
12 |
13 | import _ from './passport';
14 | import passport from 'koa-passport';
15 |
16 | import log4js from 'log4js';
17 |
18 | export default function middleware(app) {
19 |
20 | app.proxy = true;
21 |
22 | log4js.configure({
23 | appenders: [
24 | { type: 'console' },
25 | { type: 'dateFile', filename: __dirname + '/../tmp/boilerplate.log' , "pattern":"-yyyy-MM-dd-hh.log","alwaysIncludePattern":false, category: 'file' }
26 | ],
27 | replaceConsole: true
28 | });
29 |
30 | app.use(cors({ credentials: true }));
31 | app.use(convert(Logger()))
32 | app.use(bodyParser())
33 | app.use(mount("/", convert(Serve(__dirname + '/../public/'))));
34 |
35 | app.keys = ['superalsrk-session-key'];
36 | app.use(convert(session()))
37 |
38 | app.use(passport.initialize())
39 | app.use(passport.session())
40 |
41 | app.use(views(__dirname + '/../views', {extension: 'swig'}))
42 |
43 | }
--------------------------------------------------------------------------------
/src/models/account.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import db from '../lib/db';
4 | import log4js from 'log4js';
5 |
6 | const Account = {}
7 | const LOG = log4js.getLogger('file');
8 |
9 | Account.findOne = function (id, cb) {
10 | // db.queryAsync('select * from t_account where id = ?', [id], function(err, results) {
11 | // if(results == null || results.length == 0) {
12 | // err = Error('empty')
13 | // }
14 | // cb(err, results[0])
15 | // })
16 |
17 | //Mock Scripts
18 | const account = {"id": 1, "username" : "test", "password" : "test"}
19 | cb(null, account)
20 |
21 | }
22 |
23 | Account.verify = async function(username, password) {
24 |
25 | //let account = await db.query('select * from t_account where email = ?', [username])
26 |
27 | LOG.warn(JSON.stringify({
28 | 'username' : username,
29 | 'password' : password
30 | }))
31 |
32 | //Mock Scripts
33 | let account = [{"id": 1, "username" : "test", "password" : "test"}]
34 |
35 | if(account == null || account.length != 1) {
36 | return null;
37 | } else{
38 | if((account[0].password == password) && (account[0].username == username)) {
39 | return account[0];
40 | } else {
41 | return null;
42 | }
43 | }
44 | }
45 |
46 | export default Account;
--------------------------------------------------------------------------------
/app/reducers/main.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes';
2 |
3 | const initState = {
4 |
5 | avgTime : '0',
6 | bounceRate : '0',
7 | totalVisitors : '0',
8 | uniqueVisitors : '0',
9 | uv : [],
10 | pv : [],
11 | legends : [],
12 |
13 | chartShow : true
14 |
15 | }
16 |
17 |
18 | const main = (state = initState, action) => {
19 | switch(action.type) {
20 | case types.MAIN_RECEIVE_SUMMARY: {
21 | return Object.assign({}, state, {
22 | avgTime : action.data.avgTime,
23 | bounceRate : action.data.bounceRate,
24 | totalVisitors : action.data.totalVisitors,
25 | uniqueVisitors : action.data.uniqueVisitors,
26 | });
27 | }
28 |
29 | case types.MAIN_RECEIVE_LINECHART: {
30 | return Object.assign({}, state, {
31 | uv : action.data.uv,
32 | pv : action.data.pv,
33 | legends : action.data.legends
34 | })
35 | }
36 |
37 | case types.MAIN_LINECHART_DELETED : {
38 | return Object.assign({}, state, {
39 | chartShow : false
40 | })
41 | }
42 |
43 | default:
44 | return state;
45 | }
46 | }
47 |
48 | export default main;
49 |
--------------------------------------------------------------------------------
/test/test.spec.js:
--------------------------------------------------------------------------------
1 | import app from '../src/app';
2 | import supertest from 'supertest'
3 |
4 | const request = supertest.agent(app.listen())
5 |
6 | describe('Test Open API', function () {
7 | it('should be "{status : test}"', function (done) {
8 | request
9 | .get('/open/test')
10 | .expect('Content-Type', 'application/json; charset=utf-8')
11 | .expect(200)
12 | .expect({'status' : 'done'})
13 | .end(function(err, res) {
14 | if(err) return done(err)
15 | done()
16 | })
17 | })
18 | })
19 |
20 |
21 | describe('Test Auth API', function() {
22 | it('should not auth', function(done) {
23 | request
24 | .get('/api/app')
25 | .expect(200)
26 | .expect({'status' : 401})
27 | .end(function(err, res) {
28 | if(err) return done(err)
29 | done()
30 | })
31 | })
32 |
33 | it('should login success', function(done) {
34 | request
35 | .post('/auth/login')
36 | .send({'username':'test', 'password':'test'})
37 | .expect(200)
38 | .expect({'user':{'id':1, 'username':'test', 'password':'test'}})
39 | .end(function(err, res) {
40 | if(err) return done(err)
41 | done()
42 | })
43 | })
44 |
45 | it('should auth', function(done) {
46 | request
47 | .get('/api/app')
48 | .expect(200)
49 | .expect({'status' : 'app'})
50 | .end(function(err, res) {
51 | if(err) return done(err)
52 | done()
53 | })
54 | })
55 | })
56 |
--------------------------------------------------------------------------------
/src/views/main.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Koa2-boilerplate
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {% if process.env.NODE_ENV !== "development" %}
25 |
26 |
27 | {% else %}
28 |
29 |
30 | {% endif %}
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/components/Leftbar.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | class Leftbar extends React.Component {
4 |
5 | render() {
6 | return (
7 |
45 | )
46 | }
47 | }
48 |
49 | export default Leftbar;
--------------------------------------------------------------------------------
/app/containers/DashboardContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { connect } from 'react-redux';
4 | import { checkAuth } from '../actions/authed';
5 |
6 | import Header from '../components/Header';
7 | import Leftbar from '../components/Leftbar';
8 | import Github from '../components/Github';
9 | import Summary from '../components/Summary';
10 | import Trend from '../components/Trend';
11 |
12 | class DashboardContainer extends React.Component {
13 | componentDidMount() {
14 | document.body.classList.remove('bg-white')
15 | document.getElementById('page-container').classList.add('page-sidebar-fixed')
16 | document.getElementById('page-container').classList.add('page-header-fixed')
17 |
18 | if(this.props.route.path != '/404') {
19 | this.props.dispatch(checkAuth())
20 | }
21 |
22 | }
23 |
24 | render() {
25 |
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Dashboard header small text goes here...
35 |
36 |
37 |
38 |
39 | )
40 | }
41 | }
42 |
43 |
44 | function mapStateToProps(state) {
45 |
46 | return {
47 | main : state.main
48 | }
49 | }
50 |
51 | export default connect(mapStateToProps)(DashboardContainer)
--------------------------------------------------------------------------------
/app/actions/authed.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes';
2 | import $ from 'jquery';
3 | import history from '../store/history';
4 |
5 | import 'whatwg-fetch';
6 |
7 | export function checkAuth() {
8 | return dispatch => {
9 | fetch('/auth/status', {
10 | credentials: 'same-origin'
11 | })
12 | .then( tresponse => {
13 | return tresponse.json()
14 | })
15 | .then( tjson => {
16 |
17 | if(tjson.isLogin == false) {
18 | history.push('/signin')
19 | }
20 | })
21 | }
22 | }
23 |
24 | export function authUser(authinfo) {
25 | return dispatch => {
26 | $.ajax('/auth/login', {
27 | type: "POST",
28 | contentType: "application/json; charset=utf-8",
29 | data : JSON.stringify(authinfo),
30 | async : true,
31 | success: function(data, status, xhr) {
32 | if(data.status == 400) {
33 | alert('Login errro, username/password is test/test')
34 | dispatch({
35 | type : types.AUTH_FAILED
36 | })
37 | } else {
38 | dispatch({
39 | type : types.AUTH_SUCCESS
40 | })
41 | history.push('/main')
42 | }
43 | },
44 | xhrFields: {
45 | withCredentials: true
46 | },
47 | crossDomain: true
48 | });
49 | }
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
3 | var path = require('path');
4 |
5 | module.exports = {
6 |
7 | entry: {
8 | main : './app/index',
9 | vendor : [
10 | 'react',
11 | 'redux',
12 | 'react-redux',
13 | 'react-dom',
14 | 'jquery',
15 | 'whatwg-fetch'
16 | ]
17 | },
18 | output: {
19 | path: path.join(__dirname, 'src/public/assets'),
20 | filename: '[name].js',
21 | publicPath: 'http://localhost:3000/static/',
22 | },
23 | plugins: [
24 |
25 | new webpack.optimize.CommonsChunkPlugin('vendor', './vendor.bundle.js'),
26 | new webpack.optimize.UglifyJsPlugin({
27 | compress: {
28 | //supresses warnings, usually from module minification
29 | warnings: false
30 | }
31 | }),
32 | new webpack.DefinePlugin({
33 | "process.env": {
34 | NODE_ENV: JSON.stringify("production")
35 | }})
36 |
37 | ],
38 | module: {
39 | loaders: [
40 | {
41 | test: /(\.jsx|\.js)$/,
42 | loaders: ['babel?presets[]=es2015&presets[]=react'],
43 | exclude: /node_modules/,
44 | include: __dirname
45 | },
46 | {
47 | test: /\.(css|less)$/,
48 | loader: 'null'
49 | },
50 | { test: /\.woff2?$/, loader: 'null' },
51 | { test: /\.ttf$/, loader: 'null' },
52 | { test: /\.eot$/, loader: 'null' },
53 | { test: /\.svg$/, loader: 'null' },
54 | { test: /\.(png|jpg|jpeg|gif|webp)$/i, loader: 'url?limit=200' },
55 | { test: /\.json$/, loader: 'json' }
56 | ],
57 | },
58 | resolve: {
59 | extensions: ['', '.js', '.jsx'],
60 | }
61 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
3 | var path = require('path');
4 |
5 | module.exports = {
6 | devtool: 'cheap-module-eval-source-map',
7 | entry: {
8 |
9 | main : [
10 | 'webpack-hot-middleware/client?http:localhost:9000',
11 | 'webpack/hot/only-dev-server',
12 | './app/index'
13 | ],
14 | vendor : [
15 | 'react',
16 | 'redux',
17 | 'react-redux',
18 | 'react-dom',
19 | 'jquery',
20 | 'whatwg-fetch',
21 | ]
22 | },
23 | output: {
24 | publicPath: "http://0.0.0.0:9000/assets/",
25 | path: path.join(__dirname, 'src/public/assets'),
26 | filename: '[name].js'
27 | },
28 | plugins: [
29 |
30 | new webpack.optimize.CommonsChunkPlugin('vendor', './vendor.bundle.js'),
31 | new webpack.DefinePlugin({
32 | "process.env": {
33 | NODE_ENV: JSON.stringify("development")
34 | }}),
35 | new webpack.HotModuleReplacementPlugin()
36 | ],
37 | module: {
38 | loaders: [
39 | {
40 | test: /(\.jsx|\.js)$/,
41 | loaders: ['babel?presets[]=es2015&presets[]=react'],
42 | exclude: /node_modules/,
43 | include: __dirname
44 | },
45 | {
46 | test: /\.(css|less)$/,
47 | loader: 'null'
48 | },
49 | { test: /\.woff2?$/, loader: 'null' },
50 | { test: /\.ttf$/, loader: 'null' },
51 | { test: /\.eot$/, loader: 'null' },
52 | { test: /\.svg$/, loader: 'null' },
53 | { test: /\.(png|jpg|jpeg|gif|webp)$/i, loader: 'url?limit=200' },
54 | { test: /\.json$/, loader: 'json' }
55 | ],
56 | },
57 | resolve: {
58 | extensions: ['', '.js', '.jsx'],
59 | }
60 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | koa2-boilerplate
2 | ====
3 |
4 |   [](https://hub.docker.com/r/superalsrk/koa2-boilerplate/builds/) [](https://microbadger.com/images/superalsrk/koa2-boilerplate "Get your own image badge on microbadger.com")
5 |
6 | This boilerplate shows ways to
7 |
8 | + Use mysql in Koa2
9 | + CORS Ajax request
10 | + Ajax Login and Ajax Logout in Koa2, based on [koa-passport](https://github.com/rkusa/koa-passport) and [passport-local](https://github.com/jaredhanson/passport-local)
11 | + Export excel in Koa2, based on [excel-export](https://github.com/functionscope/Node-Excel-Export)
12 | + Serve static files in Koa2
13 | + Integrate with Redux App
14 | + Use charts(D3.js, Echarts, plot) library in Redux App
15 | + Deploy Koa2 app with docker
16 |
17 | ### Preview
18 |
19 | [Online Demo](http://koa2-boilerplate.tarax.cn)
20 |
21 | ### Usage
22 |
23 | ```bash
24 | $ npm install
25 |
26 | # Start application
27 | $ npm run dev
28 |
29 | # Run test cases
30 | $ npm run test
31 |
32 | # Generate test report
33 | $ npm run report
34 | ```
35 |
36 | You can also use docker to start this boilerplate
37 |
38 | ```
39 | $ docker pull superalsrk/koa2-boilerplate
40 | $ docker run -d -p 5000:5000 superalsrk/koa2-boilerplate
41 | ```
42 |
43 | ### Develop & Deploy
44 |
45 | + [中文版](https://github.com/superalsrk/koa2-boilerplate/wiki/develop-and-deploy.md)
46 | + [English](https://github.com/superalsrk/koa2-boilerplate/wiki/develop-and-deploy-en.md)
47 |
48 | ## Screenshots
49 |
50 | + Login Page
51 |
52 | 
53 |
54 | + Dashboard
55 |
56 | 
57 |
58 | ### License
59 |
60 | MIT
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/routes/mock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Router from 'koa-router';
4 | import NodeExcel from 'excel-export';
5 |
6 | const router = new Router();
7 |
8 |
9 | router.get('/summary', async (ctx, next) => {
10 |
11 | let pv = Math.floor(Math.random() * 2000000) + 1500000;
12 | let uv = Math.floor(pv/3)
13 | let bounceRate = (Math.random()*30 + 15) + ''
14 |
15 | let milliFormat = (input) => {
16 | return input && input.toString()
17 | .replace(/(^|\s)\d+/g, (m) => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
18 | }
19 | ctx.body = {
20 | 'status' : 200,
21 | 'data' : {
22 | 'totalVisitors' : milliFormat(pv),
23 | 'bounceRate' : bounceRate.substr(0,5) + '%',
24 | 'uniqueVisitors' : milliFormat(uv),
25 | 'avgTime' : '00:12:23'
26 | }
27 | }
28 | })
29 |
30 | router.get('/export/pv', async (ctx, next) => {
31 | let conf = {}
32 | conf.cols = [{
33 | caption:'Header',
34 | type:'string',
35 | width: 145
36 | }]
37 |
38 | conf.rows = [['value01'], ['value02'], ['value03']]
39 |
40 | ctx.type = 'application/vnd.openxmlformats'
41 | ctx.attachment( "Report-PV.xlsx")
42 | ctx.body = new Buffer(NodeExcel.execute(conf), 'binary')
43 | })
44 |
45 | router.get('/export/uv', async (ctx, next) => {
46 | let conf = {}
47 | conf.cols = [{
48 | caption:'Header',
49 | type:'string',
50 | width: 145
51 | }]
52 |
53 | conf.rows = [['value01'], ['value02'], ['value03']]
54 |
55 | ctx.type = 'application/vnd.openxmlformats'
56 | ctx.attachment( "Report-UV.xlsx")
57 | ctx.body = new Buffer(NodeExcel.execute(conf), 'binary')
58 | })
59 |
60 |
61 | router.get('/linechart', async (ctx, next) => {
62 |
63 | let origin = []
64 | for (let i = 0; i < 20; i++) {
65 | origin.push(Math.floor(Math.random() * 300) + 90 )
66 | }
67 |
68 | let pv = origin.map( (i, index) => {return [index+1, i]})
69 | let uv = origin.map( (i, index) => {return [index+1, Math.floor(i/3.0)]})
70 | let legends = [[1, ""], [2, ""], [3, "May 15"], [4, ""], [5, ""], [6, "May 19"], [7, ""], [8, ""], [9, "May 22"], [10, ""], [11, ""], [12, "May 25"], [13, ""], [14, ""], [15, "May 28"], [16, ""], [17, ""], [18, "May 31"], [19, ""], [20, ""]];
71 |
72 | ctx.body = {
73 | 'status' : 200,
74 | 'data' : {
75 | 'pv' : pv,
76 | 'uv' : uv,
77 | 'legends' : legends
78 | }
79 | }
80 | })
81 |
82 |
83 | export default router;
--------------------------------------------------------------------------------
/app/components/Summary.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { fetchSummaryData } from '../actions/main';
3 |
4 |
5 |
6 | const propTypes = {
7 | dispatch: PropTypes.func.isRequired
8 | };
9 |
10 | class Summary extends React.Component {
11 |
12 | componentDidMount() {
13 | const { dispatch } = this.props;
14 | dispatch(fetchSummaryData())
15 | }
16 |
17 | render() {
18 |
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
TOTAL VISITORS
27 |
{ this.props.main.totalVisitors }
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
BOUNCE RATE
40 |
{ this.props.main.bounceRate }
41 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
UNIQUE VISITORS
53 |
{ this.props.main.uniqueVisitors }
54 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
AVG TIME ON SITE
66 |
{ this.props.main.avgTime }
67 |
68 |
71 |
72 |
73 |
74 | )
75 | }
76 | }
77 |
78 | Summary.propTypes = propTypes;
79 | export default Summary
80 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "koa2-boilerplate",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "start.js",
6 | "scripts": {
7 | "dev": "webpack-dev-server --progress --colors --hot --host 0.0.0.0 --port 9000 --content-base ./server/public --config ./webpack.config.js | nodemon start.js",
8 | "build": "NODE_ENV=production webpack -p --config ./webpack.prod.config.js",
9 | "test": "mocha --compilers js:babel-core/register --require babel-polyfill",
10 | "report": "mocha --compilers js:babel-core/register --require babel-polyfill --reporter mochawesome"
11 | },
12 | "keywords": [
13 | "koa2-boilerplate",
14 | "koa2 export excel",
15 | "koa2 login with passport.js",
16 | "koa2 cors rest request",
17 | "koa2 with redux or vue",
18 | "koa2 with docker"
19 | ],
20 | "author": "SRK.Lyu (http://stackbox.org)",
21 | "license": "MIT",
22 | "dependencies": {
23 | "amqplib": "^0.4.2",
24 | "bcryptjs": "^2.3.0",
25 | "excel-export": "^0.5.1",
26 | "fivebeans": "^1.4.1",
27 | "handlebars": "^4.0.5",
28 | "hashids": "^1.1.0",
29 | "kcors": "^2.1.1",
30 | "koa": "^2.0.0",
31 | "koa-bodyparser": "^3.1.0",
32 | "koa-compose": "^3.1.0",
33 | "koa-convert": "^1.2.0",
34 | "koa-generic-session": "^1.11.3",
35 | "koa-logger": "^1.3.0",
36 | "koa-mount": "^2.0.0",
37 | "koa-passport": "^2.2.1",
38 | "koa-redis": "^2.1.1",
39 | "koa-router": "^7.0.1",
40 | "koa-static": "^2.0.0",
41 | "koa-views": "^5.0.2",
42 | "lodash": "^4.14.1",
43 | "log4js": "^0.6.38",
44 | "mysql": "^2.11.1",
45 | "node-excel": "^0.5.0",
46 | "passport-local": "^1.0.0",
47 | "path": "^0.12.7",
48 | "request": "^2.73.0",
49 | "swig": "^1.4.2"
50 | },
51 | "devDependencies": {
52 | "babel-cli": "^6.10.1",
53 | "babel-core": "^6.10.4",
54 | "babel-loader": "^6.2.4",
55 | "babel-polyfill": "^6.9.1",
56 | "babel-preset-es2015": "^6.9.0",
57 | "babel-preset-react": "^6.11.1",
58 | "babel-preset-stage-0": "^6.5.0",
59 | "css-loader": "^0.23.1",
60 | "file-loader": "^0.9.0",
61 | "jquery": "^3.1.0",
62 | "mocha": "^3.0.2",
63 | "mochawesome": "^1.5.1",
64 | "nodemon": "^1.10.0",
65 | "react": "^15.3.0",
66 | "react-dom": "^15.3.0",
67 | "react-github-fork-ribbon": "^0.4.4",
68 | "react-hot-loader": "^1.3.0",
69 | "react-redux": "^4.4.5",
70 | "react-router": "^2.5.2",
71 | "react-router-redux": "^4.0.5",
72 | "redux": "^3.5.2",
73 | "redux-logger": "^2.6.1",
74 | "redux-router": "^1.0.0",
75 | "redux-thunk": "^2.1.0",
76 | "serve-favicon": "^2.3.0",
77 | "should": "^11.1.0",
78 | "style-loader": "^0.13.1",
79 | "supertest": "^2.0.0",
80 | "url-loader": "^0.5.7",
81 | "webpack": "^1.13.1",
82 | "webpack-dev-middleware": "^1.6.1",
83 | "webpack-dev-server": "^1.14.1",
84 | "webpack-hot-middleware": "^2.12.2",
85 | "whatwg-fetch": "^1.0.0"
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/node,eclipse,intellij
3 |
4 | ### Node ###
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # node-waf configuration
29 | .lock-wscript
30 |
31 | # Compiled binary addons (http://nodejs.org/api/addons.html)
32 | build/Release
33 |
34 | # Dependency directories
35 | node_modules
36 | jspm_packages
37 |
38 | # Optional npm cache directory
39 | .npm
40 |
41 | # Optional REPL history
42 | .node_repl_history
43 |
44 | .log
45 |
46 |
47 | ### Eclipse ###
48 |
49 | .metadata
50 | bin/
51 | *.tmp
52 | *.bak
53 | *.swp
54 | *~.nib
55 | local.properties
56 | .settings/
57 | .loadpath
58 | .recommenders
59 |
60 | # Eclipse Core
61 | .project
62 |
63 | # External tool builders
64 | .externalToolBuilders/
65 |
66 | # Locally stored "Eclipse launch configurations"
67 | *.launch
68 |
69 | # PyDev specific (Python IDE for Eclipse)
70 | *.pydevproject
71 |
72 | # CDT-specific (C/C++ Development Tooling)
73 | .cproject
74 |
75 | # JDT-specific (Eclipse Java Development Tools)
76 | .classpath
77 |
78 | # Java annotation processor (APT)
79 | .factorypath
80 |
81 | # PDT-specific (PHP Development Tools)
82 | .buildpath
83 |
84 | # sbteclipse plugin
85 | .target
86 |
87 | # Tern plugin
88 | .tern-project
89 |
90 | # TeXlipse plugin
91 | .texlipse
92 |
93 | # STS (Spring Tool Suite)
94 | .springBeans
95 |
96 | # Code Recommenders
97 | .recommenders/
98 |
99 |
100 | ### Intellij ###
101 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
102 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
103 |
104 | # User-specific stuff:
105 | .idea/workspace.xml
106 | .idea/tasks.xml
107 | .idea/dictionaries
108 | .idea/vcs.xml
109 | .idea/jsLibraryMappings.xml
110 |
111 | # Sensitive or high-churn files:
112 | .idea/dataSources.ids
113 | .idea/dataSources.xml
114 | .idea/dataSources.local.xml
115 | .idea/sqlDataSources.xml
116 | .idea/dynamic.xml
117 | .idea/uiDesigner.xml
118 |
119 | # Gradle:
120 | .idea/gradle.xml
121 | .idea/libraries
122 |
123 | # Mongo Explorer plugin:
124 | .idea/mongoSettings.xml
125 |
126 | ## File-based project format:
127 | *.iws
128 |
129 | ## Plugin-specific files:
130 |
131 | # IntelliJ
132 | /out/
133 |
134 | # mpeltonen/sbt-idea plugin
135 | .idea_modules/
136 |
137 | # JIRA plugin
138 | atlassian-ide-plugin.xml
139 |
140 | # Crashlytics plugin (for Android Studio and IntelliJ)
141 | com_crashlytics_export_strings.xml
142 | crashlytics.properties
143 | crashlytics-build.properties
144 | fabric.properties
145 |
146 | ### Intellij Patch ###
147 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
148 |
149 | # *.iml
150 | # modules.xml
151 | # .idea/misc.xml
152 | # *.ipr
153 | vendor.bundle.js
154 |
155 | mochawesome-reports
156 |
--------------------------------------------------------------------------------
/app/containers/LoginContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { authUser } from '../actions/authed';
4 |
5 | import Github from '../components/Github';
6 |
7 | class LoginContainer extends React.Component {
8 | onSubmit(event) {
9 | event.preventDefault();
10 | this.props.dispatch(authUser({username:this.refs.username.value, password :this.refs.password.value}))
11 | }
12 |
13 | componentDidMount() {
14 | document.body.classList.add('bg-white')
15 | document.getElementById('page-container').classList.remove('page-sidebar-fixed')
16 | document.getElementById('page-container').classList.remove('page-header-fixed')
17 | }
18 |
19 | render() {
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
Announcing the Color Admin app
30 |
31 | Download the Color Admin app for iPhone®, iPad®, and Android™. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Color Admin
41 | responsive bootstrap 3 admin template
42 |
43 |
44 |
45 |
46 |
47 |
48 |
73 |
74 |
75 | )
76 | }
77 | }
78 |
79 | export default connect()(LoginContainer)
--------------------------------------------------------------------------------
/app/components/Trend.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { fetchMainChartData , deleteLineChart } from '../actions/main';
3 | import ReactDOM from 'react-dom';
4 |
5 | class Trend extends React.Component {
6 |
7 | componentDidMount() {
8 | //this.renderChart()
9 | this.props.dispatch(fetchMainChartData())
10 | }
11 |
12 | componentDidUpdate(prevProps) {
13 | console.log('Did update', prevProps, this.props)
14 |
15 | if(prevProps.main.pv && prevProps.main.pv === this.props.main.pv) {
16 | return
17 | }
18 |
19 | this.renderChart(this.props.main.pv, this.props.main.uv, this.props.main.legends)
20 |
21 | }
22 |
23 |
24 |
25 | renderChart(pv=[], uv=[], legends=[]) {
26 | var chartDiv = this.refs.interchart;
27 |
28 | var t = pv;
29 | var n = uv;
30 | var r = legends;
31 |
32 | const blue = "#348fe2"
33 | const green = "#00acac";
34 |
35 | $.plot(chartDiv, [{
36 | data: t,
37 | label: "Page Views",
38 | color: blue,
39 | lines: {
40 | show: true,
41 | fill: false,
42 | lineWidth: 2
43 | },
44 | points: {
45 | show: true,
46 | radius: 3,
47 | fillColor: "#fff"
48 | },
49 | shadowSize: 0
50 | }, {
51 | data: n,
52 | label: "Visitors",
53 | color: green,
54 | lines: {
55 | show: true,
56 | fill: false,
57 | lineWidth: 2
58 | },
59 | points: {
60 | show: true,
61 | radius: 3,
62 | fillColor: "#fff"
63 | },
64 | shadowSize: 0
65 | }], {
66 | xaxis: {
67 | ticks: r,
68 | tickDecimals: 0,
69 | tickColor: "#ddd"
70 | },
71 | yaxis: {
72 | ticks: 10,
73 | tickColor: "#ddd",
74 | min: 0,
75 | max: 400
76 | },
77 | grid: {
78 | hoverable: true,
79 | clickable: true,
80 | tickColor: "#ddd",
81 | borderWidth: 1,
82 | backgroundColor: "#fff",
83 | borderColor: "#ddd"
84 | },
85 | legend: {
86 | labelBoxBorderColor: "#ddd",
87 | margin: 10,
88 | noColumns: 1,
89 | show: true
90 | }
91 | });
92 | }
93 |
94 |
95 | render() {
96 |
97 | if(this.props.main.chartShow) {
98 | return (
99 |
100 |
101 |
102 |
103 |
107 |
Website Analytics (Last 7 Days)
108 |
109 |
112 |
116 |
117 |
118 |
119 | )
120 | } else {
121 | return (
122 |
123 | )
124 | }
125 | }
126 | }
127 |
128 | export default Trend
129 |
--------------------------------------------------------------------------------