├── .dockerignore ├── .editorconfig ├── .env ├── .gitignore ├── CHANGELOG.md ├── README.md ├── app ├── app.ts ├── config │ ├── default.json │ └── production.json ├── hooks │ └── index.ts ├── middleware │ ├── index.ts │ ├── logger.ts │ ├── not-found-handler.ts │ └── webpack-hot.ts ├── models │ ├── message.ts │ └── user.ts ├── service │ ├── authentication │ │ ├── hooks │ │ │ └── index.ts │ │ └── index.ts │ ├── index.ts │ ├── message │ │ ├── hooks │ │ │ └── index.ts │ │ └── index.ts │ └── user │ │ ├── hooks │ │ └── index.ts │ │ └── index.ts └── webpack │ └── webpack.server.common.ts ├── client ├── app │ ├── 404 │ │ ├── 404.component.html │ │ ├── 404.component.scss │ │ └── 404.component.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.global.scss │ ├── app.module.ts │ ├── app.routes.ts │ ├── app.service.ts │ ├── env.ts │ ├── index.ts │ ├── login │ │ ├── login.component.html │ │ ├── login.component.scss │ │ ├── login.component.ts │ │ └── login.service.ts │ ├── meta.json │ ├── module │ │ ├── dashboard │ │ │ ├── dashboard.component.html │ │ │ ├── dashboard.component.ts │ │ │ ├── dashboard.module.ts │ │ │ ├── dashboard.routes.ts │ │ │ └── index.ts │ │ ├── home │ │ │ ├── home.component.html │ │ │ └── home.component.ts │ │ ├── index.ts │ │ ├── message │ │ │ ├── index.ts │ │ │ ├── message.component.html │ │ │ ├── message.component.ts │ │ │ ├── message.module.ts │ │ │ ├── message.routes.ts │ │ │ └── message.service.ts │ │ ├── mod.module.ts │ │ ├── module.component.html │ │ ├── module.component.scss │ │ ├── module.component.ts │ │ ├── module.routes.ts │ │ ├── setting │ │ │ ├── password.component.html │ │ │ ├── password.component.scss │ │ │ ├── password.component.ts │ │ │ ├── profile.component.html │ │ │ ├── profile.component.ts │ │ │ ├── setting.module.ts │ │ │ └── setting.routes.ts │ │ └── user │ │ │ ├── dialog │ │ │ ├── addUser.html │ │ │ └── addUser.scss │ │ │ ├── index.ts │ │ │ ├── user.component.html │ │ │ ├── user.component.scss │ │ │ ├── user.component.ts │ │ │ ├── user.module.ts │ │ │ ├── user.routes.ts │ │ │ └── user.service.ts │ ├── shared │ │ ├── footer │ │ │ ├── footer.component.html │ │ │ ├── footer.component.scss │ │ │ └── footer.component.ts │ │ ├── index.ts │ │ ├── material.scss │ │ ├── module.service.ts │ │ ├── navigation.service.ts │ │ └── version.service.ts │ ├── signup │ │ ├── signup.component.html │ │ ├── signup.component.scss │ │ ├── signup.component.ts │ │ └── signup.service.ts │ └── widgets │ │ ├── index.ts │ │ └── todo │ │ ├── index.ts │ │ ├── todo.html │ │ └── todo.ts ├── assets │ ├── favicon.ico │ └── icon │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png ├── config │ ├── config.common.ts │ ├── config.dev.ts │ ├── config.prod.ts │ ├── config.ts │ ├── empty.ts │ ├── helpers.ts │ ├── html-elements-plugin │ │ └── index.ts │ ├── html-head-config.ts │ ├── resource-override.ts │ ├── webpack.common.ts │ ├── webpack.dev.ts │ └── webpack.prod.ts ├── custom-typings.d.ts ├── index.html ├── main.aot.ts ├── main.ts ├── polyfills.ts └── reducer │ ├── index.ts │ └── message.ts ├── docker ├── dev.dockerfile ├── docker-compose.dev.yml ├── docker-compose.prod.yml └── prod.dockerfile ├── package.json ├── process.yml ├── server.ts ├── tsconfig.client.json ├── tsconfig.server.json ├── tslint.json ├── webpack.config.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=famn 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Dependency directory 11 | # Commenting this out is preferred by some people, see 12 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 13 | /node_modules/ 14 | /typings/ 15 | 16 | # Users Environment Variables 17 | .DS_Store 18 | .lock-wscript 19 | .tsdrc 20 | .typingsrc 21 | .awcache 22 | yarn-error.log 23 | npm-debug.log 24 | 25 | 26 | # IDE configuration files 27 | .idea 28 | .vscode 29 | *.iml 30 | 31 | # target 32 | dist 33 | dll 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.0 (2017-01-21) 2 | 3 | ### Bug Fix 4 | 5 | - Change 404 page style 6 | 7 | ### Features 8 | 9 | - User can change password 10 | - User can update profile 11 | - Add createdAt, updatedAt in mongoose user schema 12 | 13 | # 0.1.1 (2017-02-21) 14 | 15 | ### Features 16 | 17 | - Update angular2 to 2.4.8 18 | - Introducing Webpack DLL 19 | - Introducing AOT (Not passed test yet) 20 | 21 | # 0.1.2 (2017-03-10) 22 | 23 | ### Features 24 | 25 | - introduce feathers-permissions to add user authorization 26 | 27 | # 0.1.3 (2017-03-17) 28 | 29 | ### Features 30 | 31 | - send message in message panel -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # famn 2 | 3 | Famn is an Angular2 application framework with both client side and server side integrated. 4 | I have been exploring for such an Angular2 [MEAN](http://mean.io) for a while but w/o an ideal solution, so I write it. 5 | 6 | Famn stands for [Feathers](http://feathersjs.com/), [Angular2](https://angular.io), [MongoDB](https://www.mongodb.com/), and [Node.js](https://nodejs.org/en/) 7 | 8 | Famn borrowed much from [angular2-webpack-starter](https://github.com/AngularClass/angular2-webpack-starter), [@angular/material](https://github.com/angular/material2), [ng2-material](https://github.com/justindujardin/ng2-material) 9 | 10 | *IMPORTANT* This project has no commercial level of support and it's suggested to use in development for quick prototype. PR is welcome to make it go further. 11 | 12 | 13 | ### Features 14 | 15 | - Angular 2 + typescript 16 | - Webpack for both client and server side 17 | - Feathers is to provide realtime service 18 | - All websocket based communications 19 | - MongoDB and mongoose model 20 | - Ngrx/store for state management 21 | - HMR in development 22 | - Material design 23 | - Docker based 24 | 25 | ### Development 26 | 27 | #### local 28 | 29 | ```sh 30 | # prepare environment 31 | npm i nodemon ts-node typescript@2.0 32 | 33 | # or use yarn 34 | yarn add nodemon ts-node typescript@2.0 35 | 36 | # build client code 37 | yarn run build:client:dev 38 | 39 | # start server with webpack hmr 40 | yarn run start:hmr 41 | ``` 42 | 43 | #### docker (recommended) 44 | 45 | ```sh 46 | # docker way which is recommended 47 | docker-compose -f ./docker/docker-compose.dev.yml up --build 48 | 49 | # real time service, check the new message in message module after running below command 50 | curl -H 'Content-Type: application/json' \ 51 | --data-binary '{ "email": "yourname@yourdomain.com", "message": "Hello FAMN" }' \ 52 | http://localhost:3030/messages/' 53 | ``` 54 | 55 | Go to `http://localhost:3030` with default created user `mo@po.da`, password `do` 56 | 57 | ### Deploy 58 | 59 | ```sh 60 | docker-compose -f ./docker/docker-compose.prod.yml up --build -d 61 | ``` 62 | 63 | ### Other commands 64 | 65 | #### build 66 | 67 | ```sh 68 | # build client for dev 69 | yarn run build:client:dev 70 | # build client for prod 71 | yarn run build:client:prod 72 | # build client with AOT for prod 73 | yarn run build:client:aot:prod 74 | # build server for prod 75 | yarn run build:server:prod 76 | ``` 77 | 78 | 79 | 80 | ### To do 81 | 82 | - ( ) CLI for project initialization 83 | - (*) user authorization 84 | - (*) robust material design data-table (ag-grid) 85 | - ( ) charts 86 | - ( ) unit test 87 | -------------------------------------------------------------------------------- /app/app.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from 'chalk' 2 | import * as path from 'path' 3 | import * as compress from 'compression' 4 | import * as cors from 'cors' 5 | import * as bodyParser from 'body-parser' 6 | import * as mongoose from 'mongoose' 7 | const feathers = require('feathers') 8 | const serveStatic = require('feathers').static 9 | const conf = require('feathers-configuration') 10 | const hooks = require('feathers-hooks') 11 | const rest = require('feathers-rest') 12 | const socketio = require('feathers-socketio') 13 | const handler = require('feathers-errors/handler') 14 | 15 | import service from './service' 16 | import middleware from './middleware' 17 | 18 | const app = feathers() 19 | 20 | 21 | // TO-FIX 22 | // Enable webpack-hot-middleware here for development 23 | if (process.env.NODE_ENV === 'development' && process.env.HMR) { 24 | const webpackdev = require('./middleware/webpack-hot') 25 | app.configure(webpackdev.default) 26 | } 27 | 28 | app.configure(conf()); 29 | 30 | (mongoose as any).Promise = global.Promise 31 | const config = app.get('mongodb') 32 | const endpoint = `mongodb://${config.server.host}:${config.server.port}/${config.db}` 33 | 34 | mongoose.connect(endpoint) 35 | mongoose.connection.on('error', err => { 36 | console.error('connection error: ', err) 37 | process.exit(1) 38 | }) 39 | mongoose.connection.on('open', () => { 40 | console.log(`MongoDB is connected at ${endpoint}`) 41 | }) 42 | 43 | app.use(compress()) 44 | .options('*', cors()) 45 | .use(cors()) 46 | .use('/', serveStatic(app.get('public'))) 47 | .use(bodyParser.json()) 48 | .use(bodyParser.urlencoded({extended: true})) 49 | .configure(hooks()) 50 | .configure(rest()) 51 | .configure(socketio()) 52 | .configure(service) 53 | .configure(middleware) 54 | 55 | // This ADMIN user is for new development, update with strong credential or remove it in production 56 | app.service('users').create({ 57 | email: 'mo@po.da', 58 | password: 'do', 59 | roles: ['ADMIN'] 60 | }) 61 | 62 | export default app 63 | -------------------------------------------------------------------------------- /app/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "famn", 3 | "port": 3030, 4 | "mongodb": { 5 | "server": { 6 | "host": "mongo", 7 | "port": 27017 8 | }, 9 | "db": "famn" 10 | }, 11 | "public": "../../dist/client", 12 | "auth": { 13 | // node -e "console.log(require('crypto').randomBytes(64).toString('hex')" 14 | "secret": "ZJApt91sS3bPV275hfobvCTSJYEgv+1n09A8D4bGlqI9S+lq3oePV1Xwla52ZipAXgaj8X/t17/3Tl6UFi1cOQ==", 15 | "cookie": { 16 | "name": "famn-jwt" 17 | }, 18 | "jwt": { 19 | "audience": "https://yourdomain.com", 20 | "issuer": "famn", 21 | "expiresIn": "1d" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8080, 3 | "auth": { 4 | "jwt": { 5 | // Make sure you change this secret for production 6 | "secret": "FAMN_AUTH_SECRET" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/hooks/index.ts: -------------------------------------------------------------------------------- 1 | // Add any common hooks you want to share across services in here. 2 | // 3 | // Below is an example of how a hook is written and exported. Please 4 | // see http://docs.feathersjs.com/hooks/readme.html for more details 5 | // on hooks. 6 | 7 | export function myHook(options) { 8 | return hook => { 9 | console.log('My custom global hook ran. Feathers is awesome!') 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/middleware/index.ts: -------------------------------------------------------------------------------- 1 | const handler = require('feathers-errors/handler') 2 | import notFound from './not-found-handler' 3 | import logger from './logger' 4 | 5 | export default function () { 6 | // Add your custom middleware here. Remember, that 7 | // just like Express the order matters, so error 8 | // handling middleware should go last. 9 | const app = this 10 | 11 | app.use(notFound()) 12 | app.use(logger(app)) 13 | app.use(handler()) 14 | } 15 | -------------------------------------------------------------------------------- /app/middleware/logger.ts: -------------------------------------------------------------------------------- 1 | import * as bunyan from 'bunyan' 2 | 3 | const logger = bunyan.createLogger({ name: 'famn' }) 4 | 5 | export default (app) => { 6 | // Add a logger to our app object for convenience 7 | app.logger = logger 8 | 9 | return (error, req, res, next) => { 10 | if (error) { 11 | const message = `${error.code ? `(${error.code}) ` : ''}Route: ${req.url} - ${error.message}` 12 | 13 | if (error.code === 404) { 14 | logger.info(message) 15 | } else { 16 | logger.error(message) 17 | logger.info(error.stack) 18 | } 19 | } 20 | next(error) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/middleware/not-found-handler.ts: -------------------------------------------------------------------------------- 1 | const errors = require('feathers-errors') 2 | 3 | export default () => { 4 | return (req, res, next) => { 5 | next(new errors.NotFound('Page not found')) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/middleware/webpack-hot.ts: -------------------------------------------------------------------------------- 1 | import * as webpack from 'webpack' 2 | import webpackConfig from '../../client/config/webpack.dev' 3 | const compiler = webpack(webpackConfig) 4 | 5 | 6 | export default function () { 7 | const app = this 8 | 9 | app.use(require('webpack-dev-middleware')(compiler, { 10 | noInfo: true, 11 | publicPath: webpackConfig.output.publicPath 12 | })) 13 | 14 | app.use(require('webpack-hot-middleware')(compiler)) 15 | } 16 | -------------------------------------------------------------------------------- /app/models/message.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose' 2 | 3 | const messageSchema = new mongoose.Schema({ 4 | email: { 5 | type: String, 6 | required: true 7 | }, 8 | message: { 9 | type: String, 10 | default: '' 11 | } 12 | }, 13 | { timestamps: {} }) 14 | 15 | export default mongoose.model('Message', messageSchema) 16 | -------------------------------------------------------------------------------- /app/models/user.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose' 2 | 3 | const userSchema = new mongoose.Schema({ 4 | email: { 5 | type: String, 6 | required: true, 7 | unique: true 8 | }, 9 | password: { 10 | type: String, 11 | required: true 12 | }, 13 | roles: { 14 | type: [String], 15 | default: [] 16 | } 17 | }, { 18 | timestamps: { 19 | createdAt: 'created_at', 20 | updatedAt: 'updated_at' 21 | } 22 | }) 23 | 24 | export default mongoose.model('User', userSchema) 25 | -------------------------------------------------------------------------------- /app/service/authentication/hooks/index.ts: -------------------------------------------------------------------------------- 1 | const auth = require('feathers-authentication') 2 | 3 | const before = { 4 | create: [ 5 | auth.hooks.authenticate(['jwt', 'local']) 6 | ], 7 | remove: [ 8 | auth.hooks.authenticate('jwt') 9 | ] 10 | } 11 | 12 | export default { 13 | before 14 | } 15 | -------------------------------------------------------------------------------- /app/service/authentication/index.ts: -------------------------------------------------------------------------------- 1 | const authentication = require('feathers-authentication') 2 | const jwt = require('feathers-authentication-jwt') 3 | const local = require('feathers-authentication-local') 4 | import hooks from './hooks' 5 | 6 | 7 | 8 | export default function() { 9 | const app = this 10 | 11 | let config = app.get('auth') 12 | 13 | app.configure(authentication(config)) 14 | .configure(jwt()) 15 | .configure(local()) 16 | 17 | app.service('authentication').before(hooks.before) 18 | } 19 | -------------------------------------------------------------------------------- /app/service/index.ts: -------------------------------------------------------------------------------- 1 | import authentication from './authentication' 2 | import user from './user' 3 | import message from './message' 4 | 5 | export default function () { 6 | const app = this 7 | 8 | app.configure(authentication) 9 | .configure(user) 10 | .configure(message) 11 | } 12 | -------------------------------------------------------------------------------- /app/service/message/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import * as globalHooks from '../../../hooks' 2 | // const auth = require('feathers-authentication') 3 | 4 | 5 | 6 | 7 | const before = { 8 | all: [ 9 | // auth.hooks.authenticate('jwt') 10 | ], 11 | find: [], 12 | get: [], 13 | create: [], 14 | update: [], 15 | patch: [], 16 | remove: [] 17 | } 18 | 19 | const after = { 20 | all: [], 21 | find: [], 22 | get: [], 23 | create: [], 24 | update: [], 25 | patch: [], 26 | remove: [] 27 | } 28 | 29 | export default { 30 | before, 31 | after 32 | } 33 | -------------------------------------------------------------------------------- /app/service/message/index.ts: -------------------------------------------------------------------------------- 1 | const service = require('feathers-mongoose') 2 | import hooks from './hooks' 3 | import MessageModel from '../../models/message' 4 | 5 | export default function() { 6 | const app = this 7 | 8 | let options = { 9 | Model: MessageModel, 10 | paginate: { 11 | default: 100, 12 | max: 200 13 | } 14 | } 15 | 16 | app.use('/messages', service(options)) 17 | const messageService = app.service('messages') 18 | messageService.before(hooks.before) 19 | messageService.after(hooks.after) 20 | } 21 | -------------------------------------------------------------------------------- /app/service/user/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import * as globalHooks from '../../../hooks' 2 | const hooks = require('feathers-hooks') 3 | const auth = require('feathers-authentication') 4 | const local = require('feathers-authentication-local') 5 | const permissions = require('feathers-permissions') 6 | 7 | const adminPermission = permissions.hooks.checkPermissions({ 8 | roles: ['ADMIN'], 9 | on: 'user', 10 | field: 'roles' 11 | }) 12 | 13 | const before = { 14 | all: [ 15 | ], 16 | find: [ 17 | adminPermission, 18 | permissions.hooks.isPermitted(), 19 | auth.hooks.authenticate('jwt') 20 | ], 21 | get: [ 22 | auth.hooks.authenticate('jwt') 23 | ], 24 | create: [ 25 | adminPermission, 26 | permissions.hooks.isPermitted(), 27 | local.hooks.hashPassword() 28 | ], 29 | update: [ 30 | auth.hooks.authenticate('jwt'), 31 | local.hooks.hashPassword() 32 | ], 33 | patch: [ 34 | auth.hooks.authenticate('jwt'), 35 | local.hooks.hashPassword() 36 | ], 37 | remove: [ 38 | adminPermission, 39 | permissions.hooks.isPermitted(), 40 | auth.hooks.authenticate('jwt'), 41 | ] 42 | } 43 | 44 | const after = { 45 | all: [hooks.remove('password')], 46 | find: [], 47 | get: [], 48 | create: [], 49 | update: [], 50 | patch: [], 51 | remove: [] 52 | } 53 | 54 | export default { 55 | before, 56 | after 57 | } 58 | -------------------------------------------------------------------------------- /app/service/user/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as mongoose from 'mongoose' 3 | const service = require('feathers-mongoose') 4 | import UserModel from '../../models/user' 5 | import hooks from './hooks' 6 | 7 | 8 | 9 | export default function() { 10 | const app = this 11 | 12 | let options = { 13 | Model: UserModel, 14 | paginate: { 15 | default: 25, 16 | max: 25 17 | } 18 | } 19 | 20 | // Initialize 'users' service 21 | app.use('/users', service(options)) 22 | 23 | // Get 'users' service to build hooks 24 | const userService = app.service('users') 25 | 26 | // Set up our before hooks 27 | userService.before(hooks.before) 28 | 29 | // Set up our after hooks 30 | userService.after(hooks.after) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/webpack/webpack.server.common.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: hjl 3 | */ 4 | 5 | const webpack = require('webpack') 6 | import { helpers } from '../../client/config/helpers' 7 | 8 | /* 9 | * Webpack Plugins 10 | */ 11 | const ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin 12 | const DefinePlugin = require('webpack/lib/DefinePlugin') 13 | 14 | 15 | 16 | /* 17 | * Webpack Constants 18 | */ 19 | const ENV = process.env.ENV = process.env.NODE_ENV = 'production' 20 | const METADATA = { 21 | ENV: ENV, 22 | title: 'Implus Admin Seed - server', 23 | // baseUrl: '/' 24 | } 25 | 26 | const fs = require('fs') 27 | // Look here http://jlongster.com/Backend-Apps-with-Webpack--Part-I 28 | const nodeModules = {} 29 | fs.readdirSync('node_modules') 30 | .filter(x => ['.bin'].indexOf(x) === -1) 31 | .forEach(mod => nodeModules[mod] = 'commonjs ' + mod) 32 | 33 | /* 34 | * Webpack configuration 35 | * 36 | * See: http://webpack.github.io/docs/configuration.html#cli 37 | */ 38 | export default { 39 | target: 'node', 40 | 41 | /* 42 | * The entry point for the bundle 43 | * Our Angular.js app 44 | * 45 | * See: http://webpack.github.io/docs/configuration.html#entry 46 | */ 47 | entry: './server.ts', 48 | 49 | output: { 50 | path: helpers.root('dist/server'), 51 | filename: 'server.bundle.js' 52 | }, 53 | 54 | devtool: 'eval', 55 | externals: nodeModules, 56 | 57 | 58 | /* 59 | * Options affecting the resolving of modules. 60 | * 61 | * See: http://webpack.github.io/docs/configuration.html#resolve 62 | */ 63 | resolve: { 64 | 65 | /* 66 | * An array of extensions that should be used to resolve modules. 67 | * 68 | * See: http://webpack.github.io/docs/configuration.html#resolve-extensions 69 | */ 70 | extensions: ['.ts', '.js', '.json'], 71 | modules: [helpers.root('app'), helpers.root('node_modules')], 72 | }, 73 | 74 | /* 75 | * Options affecting the normal modules. 76 | * 77 | * See: http://webpack.github.io/docs/configuration.html#module 78 | */ 79 | module: { 80 | /* 81 | * An array of automatically applied loaders. 82 | * 83 | * IMPORTANT: The loaders here are resolved relative to the resource which they are applied to. 84 | * This means they are not resolved relative to the configuration file. 85 | * 86 | * See: http://webpack.github.io/docs/configuration.html#module-loaders 87 | */ 88 | rules: [ 89 | { 90 | test: /\.ts$/, 91 | use: [ 92 | { 93 | loader: 'awesome-typescript-loader', 94 | options: { 95 | configFileName: 'tsconfig.server.json' 96 | } 97 | }, 98 | ], 99 | exclude: [/node_modules/] 100 | }, 101 | 102 | /* 103 | * Json loader support for *.json files. 104 | * 105 | * See: https://github.com/webpack/json-loader 106 | */ 107 | { 108 | test: /\.json$/, 109 | use: 'json-loader' 110 | }, 111 | ] 112 | 113 | }, 114 | 115 | /* 116 | * Include polyfills or mocks for various node stuff 117 | * Description: Node configuration 118 | * 119 | * See: https://webpack.github.io/docs/configuration.html#node 120 | */ 121 | node: { 122 | global: true, 123 | crypto: 'empty', 124 | process: true, 125 | module: false, 126 | clearImmediate: false, 127 | setImmediate: false 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /client/app/404/404.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |

Error 404

6 |
7 | 10 | Oops! You lost your way 11 |
12 |
13 | -------------------------------------------------------------------------------- /client/app/404/404.component.scss: -------------------------------------------------------------------------------- 1 | .page-not-found { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | padding: 50px; 8 | 9 | md-card { 10 | width: 500px; 11 | padding: 0; 12 | } 13 | 14 | md-toolbar { 15 | padding: 50px 80px; 16 | height: 200px; 17 | h1 { 18 | font-weight: lighter; 19 | } 20 | } 21 | 22 | button[md-fab] { 23 | left: 80%; 24 | top: -28px 25 | } 26 | 27 | md-card-title { 28 | padding: 16px; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/app/404/404.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { Router } from '@angular/router' 3 | 4 | @Component({ 5 | selector: 'page-not-found', 6 | templateUrl: './404.component.html', 7 | styleUrls: ['./404.component.scss'] 8 | }) 9 | 10 | export class PageNotFoundComponent { 11 | constructor(private _router: Router) { } 12 | gotoHome() { 13 | this._router.navigate(['/module', 'home']) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/app/app.component.scss: -------------------------------------------------------------------------------- 1 | famn-app { 2 | position: absolute; 3 | // margin: 0; 4 | // width: 100%; 5 | // height: 100%; 6 | top: 0px; 7 | left: 0px; 8 | right: 0px; 9 | bottom: 0px; 10 | background-color: whitesmoke; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /client/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core' 2 | 3 | // import { AppStore } from './app.store' 4 | @Component({ 5 | selector: 'famn-app', 6 | templateUrl: 'app.component.html', 7 | styleUrls: ['./app.component.scss'], 8 | encapsulation: ViewEncapsulation.None 9 | }) 10 | export class AppComponent { 11 | // constructor(public appStore: AppStore) { 12 | public _isDev: boolean = ENV === 'development' ? true : false 13 | constructor() { 14 | console.log('welcome to FAMN') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/app/app.global.scss: -------------------------------------------------------------------------------- 1 | @import "./shared/material"; 2 | 3 | 4 | 5 | html, body{ 6 | height: 100%; 7 | width: 100%; 8 | margin: 0; 9 | font-family: Roboto, "Helvetica Neue", sans-serif; 10 | } 11 | 12 | h1, h2, h3, h4, h5, h6 { 13 | margin: 0px; 14 | } 15 | 16 | [md-fab].fixed-bottom-right { 17 | position: fixed; 18 | right: 60px; 19 | bottom: 100px; 20 | } 21 | -------------------------------------------------------------------------------- /client/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ApplicationRef } from '@angular/core' 2 | import { BrowserModule } from '@angular/platform-browser' 3 | import { CommonModule } from '@angular/common' 4 | import { FormsModule } from '@angular/forms' 5 | import { HttpModule } from '@angular/http' 6 | import { RouterModule } from '@angular/router' 7 | import { removeNgStyles, createNewHosts, createInputTransfer } from '@angularclass/hmr' 8 | 9 | import './app.global.scss' 10 | 11 | /** 12 | * Import ngrx 13 | */ 14 | import { compose } from '@ngrx/core/compose' 15 | import { Store, StoreModule, ActionReducer, combineReducers } from '@ngrx/store' 16 | import { StoreDevtoolsModule } from '@ngrx/store-devtools' 17 | import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor' 18 | 19 | import { MaterialModule } from '@angular/material' 20 | import { FlexLayoutModule } from '@angular/flex-layout' 21 | 22 | /** 23 | * Import toplevel component/providers/directives/pipes 24 | */ 25 | import { AppComponent } from './app.component' 26 | import { LoginComponent } from './login/login.component' 27 | import { SignupComponent } from './signup/signup.component' 28 | import { PageNotFoundComponent } from './404/404.component' 29 | import { SocketService, AuthGuard } from './app.service' 30 | import { ROUTES } from './app.routes' 31 | import { ENV_PROVIDERS } from './env' 32 | 33 | 34 | 35 | /** 36 | * Reducer for @ngrx/store 37 | */ 38 | import { message } from '../reducer' 39 | 40 | // Reset the root state for HMR 41 | function stateSetter(reducer: ActionReducer): ActionReducer { 42 | return function (state, action) { 43 | if (action.type === 'SET_ROOT_STATE') { 44 | return action.payload 45 | } 46 | return reducer(state, action) 47 | } 48 | } 49 | 50 | const rootReducer = compose(stateSetter, combineReducers)({ 51 | message 52 | }) 53 | 54 | let imports = [ 55 | BrowserModule, 56 | FormsModule, 57 | HttpModule, 58 | CommonModule, 59 | MaterialModule.forRoot(), 60 | FlexLayoutModule.forRoot(), 61 | RouterModule.forRoot(ROUTES, { 62 | useHash: true 63 | }), 64 | 65 | // StoreLogMonitorModule, 66 | StoreModule.provideStore(rootReducer) 67 | ] 68 | 69 | // Enable HMR and ngrx/devtools in hot reload mode 70 | if (process.env.NODE_ENV === 'development') imports.push(...[ 71 | StoreDevtoolsModule.instrumentStore({ 72 | monitor: useLogMonitor({ 73 | visible: false, 74 | position: 'right' 75 | }) 76 | }), 77 | StoreLogMonitorModule, 78 | ]) 79 | 80 | @NgModule({ 81 | bootstrap: [ 82 | AppComponent 83 | ], 84 | declarations: [ 85 | AppComponent, 86 | LoginComponent, 87 | SignupComponent, 88 | PageNotFoundComponent 89 | ], 90 | imports, 91 | providers: [ 92 | ENV_PROVIDERS, 93 | SocketService, 94 | AuthGuard 95 | ] 96 | }) 97 | 98 | export class AppModule { 99 | constructor(public appRef: ApplicationRef, private _store: Store) { } 100 | hmrOnInit(store) { 101 | if (!store || !store.rootState) return 102 | 103 | // restore state 104 | if (store.rootState) { 105 | this._store.dispatch({ 106 | type: 'SET_ROOT_STATE', 107 | payload: store.rootState 108 | }) 109 | } 110 | 111 | // restore input values 112 | if ('restoreInputValues' in store) { store.restoreInputValues() } 113 | this.appRef.tick() 114 | Object.keys(store).forEach(prop => delete store[prop]) 115 | } 116 | hmrOnDestroy(store) { 117 | const cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement) 118 | this._store.subscribe(s => store.rootState = s) 119 | // recreate elements 120 | store.disposeOldHosts = createNewHosts(cmpLocation) 121 | // save input values 122 | store.restoreInputValues = createInputTransfer() 123 | // remove styles 124 | removeNgStyles() 125 | } 126 | hmrAfterDestroy(store) { 127 | // display new elements 128 | store.disposeOldHosts() 129 | delete store.disposeOldHosts 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /client/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { LoginComponent } from './login/login.component' 2 | import { SignupComponent } from './signup/signup.component' 3 | import { PageNotFoundComponent } from './404/404.component' 4 | import { ModModule } from './module' 5 | import { AuthGuard } from './app.service' 6 | 7 | 8 | function loadModModule() { 9 | return ModModule 10 | } 11 | 12 | export const ROUTES = [ 13 | { 14 | path: '', 15 | redirectTo: '/login', 16 | pathMatch: 'full' 17 | }, 18 | { 19 | path: 'login', 20 | component: LoginComponent 21 | }, 22 | { 23 | path: 'signup', 24 | component: SignupComponent 25 | }, 26 | { 27 | path: 'module', 28 | loadChildren: loadModModule, 29 | canActivate: [AuthGuard] 30 | }, 31 | { 32 | path: '404-page', 33 | component: PageNotFoundComponent, 34 | }, 35 | { path: '**', redirectTo: '/404-page' } 36 | ] 37 | -------------------------------------------------------------------------------- /client/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import * as feathers from 'feathers/client' 2 | import * as socketio from 'feathers-socketio/client' 3 | const io = require('socket.io-client') 4 | const hooks = require('feathers-hooks') 5 | import * as authentication from 'feathers-authentication-client' 6 | 7 | import RxJS from 'rxjs' 8 | 9 | import { Injectable } from '@angular/core' 10 | import { Router, CanActivate } from '@angular/router' 11 | import { helpers } from '../config/helpers' 12 | 13 | @Injectable() 14 | export class SocketService { 15 | public socket: any 16 | public app: any 17 | 18 | constructor() { 19 | this.socket = io(helpers.getHost()) 20 | this.app = feathers() 21 | .configure(socketio(this.socket)) 22 | .configure(hooks()) 23 | .configure(authentication({ 24 | cookie: 'famn-jwt', 25 | storageKey: 'famn-jwt', 26 | storage: window.localStorage 27 | })) 28 | } 29 | 30 | getService(service) { 31 | return this.app.service(service) 32 | } 33 | 34 | authenticate(option?: any) { 35 | return this.app.authenticate(option) 36 | } 37 | 38 | logout() { 39 | // feathers-authentication-client will bind logout method to app 40 | // app.logout = app.passport.logout.bind(app.passport); 41 | return this.app.logout() 42 | } 43 | 44 | getUser() { 45 | return this.app.get('user') 46 | } 47 | 48 | getToken() { 49 | return this.app.get('token') 50 | } 51 | 52 | isLogin() { 53 | return this.getUser() ? true : false 54 | } 55 | } 56 | 57 | 58 | 59 | @Injectable() 60 | /** 61 | * AuthGuard service is to provide client side router authorization 62 | */ 63 | export class AuthGuard implements CanActivate { 64 | constructor(private _socketService: SocketService, private _router: Router) { } 65 | 66 | canActivate() { 67 | // if (this._socketService.isLogin()) return true 68 | // this._router.navigate(['/']) 69 | // return false 70 | return this._socketService.isLogin() || this._socketService.authenticate() 71 | .then(res => this._socketService.app.passport.verifyJWT(res.accessToken)) 72 | .then(payload => this._socketService.app.service('users').get(payload.userId)) 73 | .then(user => this._socketService.app.set('user', user)) 74 | .then(res => true) 75 | .catch(err => { 76 | this._router.navigate(['/login'] 77 | ) 78 | }) 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /client/app/env.ts: -------------------------------------------------------------------------------- 1 | 2 | // Angular 2 3 | // rc2 workaround 4 | import { enableDebugTools, disableDebugTools } from '@angular/platform-browser' 5 | import { enableProdMode, ApplicationRef } from '@angular/core' 6 | // Environment Providers 7 | let PROVIDERS: any[] = [ 8 | // common env directives 9 | ]; 10 | 11 | // Angular debug tools in the dev console 12 | // https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 13 | let _decorateModuleRef = function identity(value: T): T { return value; } 14 | 15 | if ('production' === ENV) { 16 | // Production 17 | disableDebugTools() 18 | enableProdMode() 19 | 20 | PROVIDERS = [ 21 | ...PROVIDERS, 22 | // custom providers in production 23 | ] 24 | 25 | } else { 26 | 27 | _decorateModuleRef = (modRef: any) => { 28 | const appRef = modRef.injector.get(ApplicationRef) 29 | const cmpRef = appRef.components[0] 30 | 31 | let _ng = (window).ng 32 | enableDebugTools(cmpRef); 33 | (window).ng.probe = _ng.probe; 34 | (window).ng.coreTokens = _ng.coreTokens 35 | return modRef 36 | } 37 | 38 | // Development 39 | PROVIDERS = [ 40 | ...PROVIDERS, 41 | // custom providers in development 42 | ] 43 | 44 | } 45 | 46 | export const decorateModuleRef = _decorateModuleRef 47 | 48 | export const ENV_PROVIDERS = [ 49 | ...PROVIDERS 50 | ] 51 | -------------------------------------------------------------------------------- /client/app/index.ts: -------------------------------------------------------------------------------- 1 | // import global style 2 | import './app.global.scss' 3 | 4 | export * from './app.module' 5 | -------------------------------------------------------------------------------- /client/app/login/login.component.html: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /client/app/login/login.component.scss: -------------------------------------------------------------------------------- 1 | .login { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | padding: 50px; 8 | 9 | md-card { 10 | padding: 0; 11 | } 12 | 13 | md-card-content { 14 | padding: 16px; 15 | 16 | md-button:hover { 17 | background-color: transparent; 18 | } 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /client/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { Router } from '@angular/router' 3 | import { LoginService } from './login.service' 4 | 5 | @Component({ 6 | selector: 'login-cmp', 7 | providers: [LoginService], 8 | templateUrl: 'login.component.html', 9 | styleUrls: ['./login.component.scss'] 10 | }) 11 | 12 | export class LoginComponent { 13 | public user: any = {} 14 | public showLogin: boolean = false 15 | 16 | constructor(private _router: Router, private _loginService: LoginService) {} 17 | 18 | ngOnInit() { 19 | this._loginService.loginToken() 20 | .then(res => { 21 | this._router.navigate(['/module']) 22 | }) 23 | .catch(err => { 24 | // No token in localStorage, should go local authentication way 25 | this.showLogin = true 26 | }) 27 | } 28 | 29 | onSubmit() { 30 | this._loginService.loginLocal(this.user) 31 | .then(res => { 32 | this._router.navigate(['/module']) 33 | }) 34 | .catch(err => { 35 | console.log(err) 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/app/login/login.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | import { Observable } from 'rxjs/Observable' 3 | import { SocketService } from '../app.service' 4 | 5 | interface User { 6 | email: String, 7 | password: String 8 | } 9 | 10 | @Injectable() 11 | export class LoginService { 12 | // public resource$: Observable 13 | // private _observable 14 | private _app 15 | 16 | constructor(private _socketService: SocketService) { 17 | this._app = this._socketService.app 18 | 19 | 20 | // this.item$ is a public observable for components to subscribe 21 | // this.resource$ = new Observable(observable => this._observable = observable) 22 | } 23 | 24 | 25 | // authentication from 'local' by email and password 26 | loginLocal(user: User) { 27 | let option = Object.assign({}, { strategy: 'local' }, user) 28 | return this.deriveUser(option) 29 | } 30 | 31 | // authentical from the token in localStorage 32 | loginToken(): any { 33 | return this.deriveUser({}) 34 | } 35 | 36 | deriveUser(option: any): any { 37 | return this._socketService.authenticate(option) 38 | .then(res => { 39 | return this._app.passport.verifyJWT(res.accessToken) 40 | }) 41 | .then(payload => { 42 | return this._app.service('users').get(payload.userId) 43 | }) 44 | .then(user => { 45 | return this._app.set('user', user) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/app/meta.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Dashboard", 4 | "id": "dashboard" 5 | }, 6 | { 7 | "name": "Message", 8 | "id": "message" 9 | }, 10 | { 11 | "name": "User", 12 | "id": "user" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /client/app/module/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/app/module/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import {TodoComponent, StatsComponent} from '../../../widgets' 3 | 4 | 5 | @Component({ 6 | selector: 'dashboard-module', 7 | templateUrl: 'dashboard.component.html' 8 | }) 9 | 10 | export class DashboardComponent { 11 | constructor() { } 12 | 13 | ngOnInit() { 14 | console.log('This is dashboard module') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/app/module/dashboard/dashboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { CommonModule } from '@angular/common' 3 | import { FormsModule } from '@angular/forms' 4 | import { RouterModule } from '@angular/router' 5 | 6 | /** 7 | * Import @angular/material, ng2-material 8 | */ 9 | import { MaterialModule } from '@angular/material' 10 | 11 | import { DashboardComponent } from './dashboard.component' 12 | import { TodoComponent } from '../../widgets/todo' 13 | 14 | import { ROUTES } from './dashboard.routes' 15 | 16 | 17 | @NgModule({ 18 | declarations: [ 19 | DashboardComponent, 20 | TodoComponent 21 | ], 22 | imports: [ 23 | CommonModule, 24 | FormsModule, 25 | MaterialModule.forRoot(), 26 | RouterModule.forChild(ROUTES), 27 | ] 28 | }) 29 | export class DashboardModule { 30 | constructor() { } 31 | } 32 | -------------------------------------------------------------------------------- /client/app/module/dashboard/dashboard.routes.ts: -------------------------------------------------------------------------------- 1 | import { DashboardComponent } from './dashboard.component' 2 | 3 | export const ROUTES = [ 4 | { 5 | path: '', 6 | component: DashboardComponent 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /client/app/module/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dashboard.module' 2 | -------------------------------------------------------------------------------- /client/app/module/home/home.component.html: -------------------------------------------------------------------------------- 1 |

Welcome to FAMN

2 | -------------------------------------------------------------------------------- /client/app/module/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'home-module', 5 | templateUrl: './home.component.html', 6 | }) 7 | 8 | export class HomeComponent { 9 | constructor() { } 10 | } 11 | -------------------------------------------------------------------------------- /client/app/module/index.ts: -------------------------------------------------------------------------------- 1 | export { ModModule } from './mod.module' 2 | -------------------------------------------------------------------------------- /client/app/module/message/index.ts: -------------------------------------------------------------------------------- 1 | export * from './message.module' 2 | -------------------------------------------------------------------------------- /client/app/module/message/message.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Messages 4 | 5 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | -------------------------------------------------------------------------------- /client/app/module/message/message.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy } from '@angular/core' 2 | import { MessageService } from './message.service' 3 | 4 | import { Store } from '@ngrx/store' 5 | import { Observable } from 'rxjs/Observable' 6 | 7 | import { GridOptions } from 'ag-grid/main' 8 | 9 | @Component({ 10 | selector: 'message', 11 | templateUrl: 'message.component.html', 12 | providers: [MessageService] 13 | }) 14 | 15 | export class MessageComponent implements OnInit, OnDestroy { 16 | private message$: Observable 17 | private messageSubscription: any 18 | public gridOptions: GridOptions 19 | public columnDefs: any[] 20 | public messages = [] 21 | public email: string 22 | public message: string 23 | 24 | constructor(private _store: Store, 25 | private messageService: MessageService) { 26 | 27 | this.message$ = this._store.select('message') 28 | 29 | this.messageSubscription = this.message$.subscribe((messages: any[]) => { 30 | this.messages = messages 31 | this.createDataSource() 32 | }) 33 | 34 | this.messageService.resource$.subscribe(res => { 35 | switch (res.type) { 36 | case 'find': 37 | this._store.dispatch({ 38 | type: 'MESSAGE_INIT', 39 | payload: res.messages 40 | }) 41 | break 42 | case 'created': 43 | // this._store.dispatch({ 44 | // type: 'MESSAGE_UPDATE', 45 | // payload: res.messages 46 | // }) 47 | this.messageService.findMessages() 48 | break 49 | default: 50 | break 51 | } 52 | }) 53 | 54 | // ag-grid initialization 55 | this.gridOptions = {} 56 | this.columnDefs = this.createColumnDefs() 57 | this.email = 'anonymous' 58 | this.message = '' 59 | } 60 | 61 | 62 | ngOnInit() { 63 | } 64 | 65 | ngOnDestroy() { 66 | this.messageSubscription.unsubscribe() 67 | this.messageService.off() 68 | } 69 | 70 | onGridReady() { 71 | this.gridOptions.api.sizeColumnsToFit() 72 | this.messageService.findMessages() 73 | } 74 | 75 | createColumnDefs() { 76 | return [ 77 | { 78 | headerName: 'Email', 79 | field: 'email' 80 | }, 81 | { 82 | headerName: 'Message', 83 | field: 'message' 84 | }, 85 | { 86 | headerName: 'Created Time', 87 | field: 'createdAt' 88 | }, 89 | { 90 | headerName: 'Updated Time', 91 | field: 'updatedAt' 92 | } 93 | ] 94 | } 95 | 96 | 97 | createDataSource() { 98 | if (!this.gridOptions) return 99 | let dataSource = { 100 | rowCount: -1, 101 | getRows: params => { 102 | let rowsThisPage = this.messages.slice(params.startRow, params.endRow) 103 | let lastRow = -1 104 | if (this.messages.length <= params.endRow) { 105 | lastRow = this.messages.length 106 | } 107 | params.successCallback(rowsThisPage, this.messages.length) 108 | } 109 | } 110 | 111 | this.gridOptions.api.setDatasource(dataSource) 112 | } 113 | 114 | createMessage() { 115 | this.messageService.createMessage({ 116 | email: this.email, 117 | message: this.message 118 | }) 119 | 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /client/app/module/message/message.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { CommonModule } from '@angular/common' 3 | import { FormsModule } from '@angular/forms' 4 | import { RouterModule } from '@angular/router' 5 | 6 | import { MaterialModule } from '@angular/material' 7 | import { FlexLayoutModule } from '@angular/flex-layout' 8 | 9 | 10 | import {AgGridModule} from 'ag-grid-angular/main' 11 | import 'ag-grid/dist/styles/ag-grid.css' 12 | import 'ag-grid/dist/styles/theme-material.css' 13 | 14 | 15 | import { MessageComponent } from './message.component' 16 | 17 | import { ROUTES } from './message.routes' 18 | 19 | @NgModule({ 20 | declarations: [ 21 | MessageComponent, 22 | ], 23 | imports: [ 24 | CommonModule, 25 | FormsModule, 26 | MaterialModule.forRoot(), 27 | FlexLayoutModule.forRoot(), 28 | AgGridModule.withComponents([]), 29 | RouterModule.forChild(ROUTES), 30 | ] 31 | }) 32 | export class MessageModule { 33 | constructor() { } 34 | } 35 | -------------------------------------------------------------------------------- /client/app/module/message/message.routes.ts: -------------------------------------------------------------------------------- 1 | import { MessageComponent } from './message.component' 2 | 3 | export const ROUTES = [ 4 | { 5 | path: '', 6 | component: MessageComponent 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /client/app/module/message/message.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | import { Response } from '@angular/http' 3 | import { Observable } from 'rxjs/Observable' 4 | import { SocketService } from '../../app.service' 5 | import 'rxjs/add/observable/fromPromise' 6 | 7 | 8 | @Injectable() 9 | export class MessageService { 10 | public resource$: Observable 11 | private _observable 12 | private socketMessagesService 13 | 14 | constructor(private _socketService: SocketService) { 15 | this.socketMessagesService = _socketService.getService('messages') 16 | 17 | // this.item$ is a public observable for components to subscribe 18 | this.resource$ = new Observable(observable => this._observable = observable) 19 | 20 | this.socketMessagesService.on('created', res => { 21 | this._observable.next({ 22 | type: 'created', 23 | messages: res 24 | }) 25 | }) 26 | 27 | } 28 | 29 | findMessages() { 30 | this.socketMessagesService.find({ 31 | query: { 32 | $sort: {createdAt: -1} 33 | } 34 | }).then(res => { 35 | this._observable.next({ 36 | type: 'find', 37 | messages: res.data 38 | }) 39 | }) 40 | } 41 | 42 | createMessage(data) { 43 | Observable.fromPromise(this.socketMessagesService.create(data)) 44 | .catch(this.handleError) 45 | } 46 | 47 | 48 | handleError(err: Response | any) { 49 | err = err instanceof Response ? err.json() : err.toString() 50 | console.error(err) 51 | return Observable.throw(err) 52 | } 53 | 54 | off() { 55 | this.socketMessagesService.removeAllListeners('created') 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /client/app/module/mod.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ApplicationRef } from '@angular/core' 2 | import { CommonModule } from '@angular/common' 3 | import { HttpModule } from '@angular/http' 4 | import { RouterModule } from '@angular/router' 5 | 6 | import { MaterialModule } from '@angular/material' 7 | import { FlexLayoutModule } from '@angular/flex-layout' 8 | 9 | 10 | 11 | /** 12 | * Import toplevel component/providers/directives/pipes 13 | */ 14 | import { ModComponent } from './module.component' 15 | import { FooterComponent } from '../shared/footer/footer.component' 16 | import { HomeComponent } from './home/home.component' 17 | 18 | import { ModuleService, NavigationService, VersionService } from '../shared/index' 19 | import { ROUTES } from './module.routes' 20 | 21 | const APP_PROVIDERS = [ 22 | ModuleService, 23 | NavigationService, 24 | VersionService, 25 | ] 26 | 27 | @NgModule({ 28 | declarations: [ 29 | ModComponent, 30 | FooterComponent, 31 | HomeComponent, 32 | ], 33 | imports: [ 34 | HttpModule, 35 | CommonModule, 36 | MaterialModule.forRoot(), 37 | FlexLayoutModule.forRoot(), 38 | RouterModule.forChild(ROUTES), 39 | ], 40 | providers: [ 41 | ...APP_PROVIDERS, 42 | // SocketService, 43 | // AuthGuard 44 | ] 45 | }) 46 | 47 | export class ModModule { 48 | constructor() { } 49 | } 50 | -------------------------------------------------------------------------------- /client/app/module/module.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

famn

5 |
6 | 7 | 8 | 12 | 13 | 14 |
15 |
16 | 17 | 21 | 22 | Modules   23 | {{navigation?.currentTitle}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /client/app/module/module.component.scss: -------------------------------------------------------------------------------- 1 | md-sidenav-container, 2 | .app-content { 3 | position: relative; 4 | margin: 0; 5 | width: 100%; 6 | height: 100%; 7 | 8 | .username { 9 | color: #fff; 10 | font-size: 14px; 11 | font-weight: normal; 12 | } 13 | } 14 | 15 | md-sidenav { 16 | width: 200px; 17 | box-shadow: 3px 0 6px rgba(0,0,0,.24); 18 | 19 | .menu-button { 20 | text-align: left; 21 | } 22 | } 23 | 24 | 25 | .app-content-inner { 26 | position: absolute; 27 | top: 64px; 28 | left: 0; 29 | right: 0; 30 | bottom: 0; 31 | overflow: auto; 32 | 33 | .app-content-main { 34 | padding: 30px; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/app/module/module.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | OnDestroy, 5 | AfterViewInit, 6 | ViewChild, 7 | Input, 8 | ChangeDetectorRef, 9 | Renderer, 10 | ElementRef, 11 | } from '@angular/core' 12 | import { Router } from '@angular/router' 13 | 14 | import { MdSidenav } from '@angular/material' 15 | import { ModuleMeta, ModuleService } from '../shared/module.service' 16 | import { SocketService } from '../app.service' 17 | import { Http, Response } from '@angular/http' 18 | import { NavigationService } from '../shared/navigation.service' 19 | import { Subscription } from 'rxjs/Subscription' 20 | import { Observable } from 'rxjs/Rx' 21 | 22 | @Component({ 23 | templateUrl: 'module.component.html', 24 | styleUrls: ['./module.component.scss'] 25 | // encapsulation: ViewEncapsulation.None 26 | }) 27 | export class ModComponent implements OnInit, OnDestroy, AfterViewInit { 28 | static SIDE_MENU_BREAKPOINT: string = 'gt-md' 29 | 30 | site: string = 'FAMN' 31 | version: string 32 | modules: ModuleMeta[] = [] 33 | 34 | @ViewChild('menu') private menu: MdSidenav 35 | 36 | private _isDev: boolean = ENV === 'development' ? true : false 37 | private _subscription = null 38 | private user = undefined 39 | 40 | 41 | constructor(private http: Http, 42 | // private changeDetectorRef: ChangeDetectorRef, 43 | private navigation: NavigationService, 44 | // private media: Media, 45 | element: ElementRef, 46 | renderer: Renderer, 47 | private _router: Router, 48 | private _modules: ModuleService, 49 | private _socketService: SocketService) { 50 | // Remove loading class to unset default styles 51 | renderer.setElementClass(element.nativeElement, 'loading', false) 52 | } 53 | 54 | ngOnInit() { 55 | // this.http.get('version.json').subscribe((res: Response) => { 56 | // const json = res.json(); 57 | // this.version = json.version; 58 | // this.angularVersion = json['@angular/core']; 59 | // this.linkTag = this.angularVersion.replace(/[>=^~]/g, ''); 60 | // }); 61 | this._modules.getModules().then((mods) => { 62 | this.modules = mods 63 | let title = 'famn' 64 | document.title = title 65 | this.navigation.currentTitle = title 66 | }) 67 | 68 | this.user = this._socketService.getUser() 69 | } 70 | 71 | ngAfterViewInit(): any { 72 | } 73 | 74 | ngOnDestroy(): any { 75 | // this._subscription.unsubscribe() 76 | } 77 | 78 | logout(): void { 79 | this._socketService.logout().then(res => { 80 | this._socketService.app.set('user', null) 81 | this._router.navigate(['/login']) 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /client/app/module/module.routes.ts: -------------------------------------------------------------------------------- 1 | import { ModComponent } from './module.component' 2 | import { HomeComponent } from './home/home.component' 3 | 4 | export const ROUTES = [ 5 | { 6 | path: '', 7 | component: ModComponent, 8 | children: [ 9 | { 10 | path: '', 11 | component: HomeComponent 12 | }, 13 | { 14 | path: 'setting', 15 | loadChildren: './setting/setting.module#SettingModule' 16 | }, 17 | { 18 | path: 'dashboard', 19 | loadChildren: './dashboard/index#DashboardModule' 20 | }, 21 | { 22 | path: 'message', 23 | loadChildren: './message/index#MessageModule' 24 | }, 25 | { 26 | path: 'user', 27 | loadChildren: './user/index#UserModule' 28 | } 29 | ] 30 | }, 31 | ] 32 | -------------------------------------------------------------------------------- /client/app/module/setting/password.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | 7 | visibility_off 8 | 9 | 10 | 11 | visibility_off 12 | Confirm New Password should be same as New Pasword 13 | 14 | 15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /client/app/module/setting/password.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/core/theming/theming'; 2 | .text-danger { 3 | // color: md-color($md-red, A700); 4 | color: mat-color($mat-pink, A400); 5 | } 6 | 7 | .submit { 8 | margin-top: 10px; 9 | } 10 | -------------------------------------------------------------------------------- /client/app/module/setting/password.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core' 2 | import { SocketService } from '../../app.service' 3 | import { MdSnackBar } from '@angular/material' 4 | 5 | @Component({ 6 | selector: 'setting-password-cmp', 7 | templateUrl: 'password.component.html', 8 | styleUrls: ['./password.component.scss'] 9 | }) 10 | export class PasswordComponent implements OnInit { 11 | private usersService 12 | public user 13 | public password 14 | public confirmPassword 15 | public isPasswordDiff = false 16 | 17 | constructor( 18 | private socketService: SocketService, 19 | private snackBar: MdSnackBar 20 | 21 | ) { 22 | this.usersService = socketService.getService('users') 23 | this.user = socketService.getUser() 24 | } 25 | 26 | ngOnInit() { } 27 | 28 | onSubmit() { 29 | if (this.password && this.password !== this.confirmPassword) this.isPasswordDiff = true 30 | else { 31 | this.usersService.patch(this.user._id, { 32 | password: this.password 33 | }) 34 | .then(res => { 35 | this.snackBar.open('Password has been updated', '', { 36 | duration: 800 37 | }) 38 | this.isPasswordDiff = false 39 | this.password = '' 40 | this.confirmPassword = '' 41 | }) 42 | .catch(err => { 43 | console.error(err) 44 | }) 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/app/module/setting/profile.component.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/app/module/setting/profile.component.html -------------------------------------------------------------------------------- /client/app/module/setting/profile.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'setting-profile-cmp', 5 | templateUrl: 'profile.component.html' 6 | }) 7 | export class ProfileComponent implements OnInit { 8 | constructor() { } 9 | 10 | ngOnInit() { } 11 | } 12 | -------------------------------------------------------------------------------- /client/app/module/setting/setting.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { CommonModule } from '@angular/common' 3 | import { FormsModule } from '@angular/forms' 4 | import { Routes, RouterModule } from '@angular/router' 5 | import { MaterialModule } from '@angular/material' 6 | import { FlexLayoutModule } from '@angular/flex-layout' 7 | 8 | 9 | 10 | import { ProfileComponent } from './profile.component' 11 | import { PasswordComponent } from './password.component' 12 | import { routes } from './setting.routes' 13 | 14 | @NgModule({ 15 | imports: [ 16 | CommonModule, 17 | FormsModule, 18 | RouterModule.forChild(routes), 19 | MaterialModule.forRoot(), 20 | FlexLayoutModule.forRoot(), 21 | ], 22 | exports: [], 23 | declarations: [ 24 | ProfileComponent, 25 | PasswordComponent 26 | ], 27 | providers: [], 28 | }) 29 | export class SettingModule { } 30 | -------------------------------------------------------------------------------- /client/app/module/setting/setting.routes.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { Routes, RouterModule } from '@angular/router' 3 | 4 | import { ProfileComponent } from './profile.component' 5 | import { PasswordComponent } from './password.component' 6 | 7 | export const routes: Routes = [ 8 | { 9 | path: '', 10 | children: [ 11 | { path: 'profile', component: ProfileComponent }, 12 | { path: 'password', component: PasswordComponent }, 13 | ] 14 | }, 15 | ] 16 | -------------------------------------------------------------------------------- /client/app/module/user/dialog/addUser.html: -------------------------------------------------------------------------------- 1 |

User

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{ r }} 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /client/app/module/user/dialog/addUser.scss: -------------------------------------------------------------------------------- 1 | .full-width { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /client/app/module/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user.module' 2 | -------------------------------------------------------------------------------- /client/app/module/user/user.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | {{u.email}} 5 |
6 | 9 | 10 | 13 | 16 | 17 |
18 | 19 |
{{r}}
20 |
21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /client/app/module/user/user.component.scss: -------------------------------------------------------------------------------- 1 | .user-card { 2 | margin: 10px 10px; 3 | } 4 | -------------------------------------------------------------------------------- /client/app/module/user/user.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core' 2 | import { MdDialog, MdDialogRef } from '@angular/material' 3 | 4 | import { UserService } from './user.service' 5 | 6 | @Component({ 7 | selector: 'user', 8 | templateUrl: 'user.component.html', 9 | styleUrls: ['./user.component.scss'] 10 | }) 11 | export class UserComponent implements OnInit { 12 | 13 | // users: {email: string, password: string, roles: string[]}[] = [] 14 | users: any[] 15 | 16 | constructor( 17 | private dialog: MdDialog, 18 | private userService: UserService 19 | ) { 20 | } 21 | 22 | ngOnInit() { 23 | this.findUsers() 24 | } 25 | 26 | findUsers() { 27 | this.userService.findUsers().subscribe(users => { 28 | this.users = users 29 | }) 30 | } 31 | 32 | addUser() { 33 | let newUser = { 34 | roles: [] 35 | } 36 | this.openUserDialog(newUser, 'new') 37 | .subscribe(user => { 38 | if (!user) return 39 | this.userService.createUser(user) 40 | .subscribe(res => { 41 | console.log('add user', res), 42 | this.findUsers() 43 | }) 44 | }) 45 | } 46 | 47 | 48 | removeUser(id) { 49 | this.userService.removeUser(id).subscribe(res => { 50 | console.log('remove user', res) 51 | this.findUsers() 52 | }) 53 | } 54 | 55 | updateUser(user) { 56 | this.openUserDialog(user, 'update') 57 | .subscribe(user => { 58 | if (!user) return 59 | this.userService.updateUser(user._id, user) 60 | .subscribe(res => { 61 | console.log('update user', res), 62 | this.findUsers() 63 | }) 64 | }) 65 | } 66 | 67 | openUserDialog(user, mode) { 68 | let dialogRef = this.dialog.open(DialogAddUserComponent, { 69 | width: '500px', 70 | height: '300px' 71 | }) 72 | dialogRef.componentInstance.user = JSON.parse(JSON.stringify(user)) 73 | dialogRef.componentInstance.mode = mode 74 | 75 | return dialogRef.afterClosed() 76 | } 77 | } 78 | 79 | 80 | @Component({ 81 | selector: 'dialog-user', 82 | templateUrl: './dialog/addUser.html', 83 | styleUrls: ['./dialog/addUser.scss'] 84 | }) 85 | export class DialogAddUserComponent { 86 | user: any 87 | mode: string 88 | roles: string[] 89 | 90 | constructor(public dialogRef: MdDialogRef) { 91 | this.roles = ['ADMIN', 'BUSINESS', 'OPERATION'] 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /client/app/module/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { CommonModule } from '@angular/common' 3 | import { FormsModule } from '@angular/forms' 4 | import { RouterModule } from '@angular/router' 5 | 6 | import { MaterialModule } from '@angular/material' 7 | import { FlexLayoutModule } from '@angular/flex-layout' 8 | 9 | 10 | import { UserComponent, DialogAddUserComponent } from './user.component' 11 | import { UserService } from './user.service' 12 | 13 | import { ROUTES } from './user.routes' 14 | 15 | 16 | @NgModule({ 17 | declarations: [ 18 | UserComponent, 19 | DialogAddUserComponent, 20 | ], 21 | entryComponents: [ 22 | DialogAddUserComponent, 23 | ], 24 | providers: [ 25 | UserService 26 | ], 27 | imports: [ 28 | CommonModule, 29 | FormsModule, 30 | MaterialModule.forRoot(), 31 | FlexLayoutModule.forRoot(), 32 | RouterModule.forChild(ROUTES), 33 | ], 34 | }) 35 | export class UserModule { 36 | constructor() { } 37 | } 38 | -------------------------------------------------------------------------------- /client/app/module/user/user.routes.ts: -------------------------------------------------------------------------------- 1 | import { UserComponent } from './user.component' 2 | 3 | export const ROUTES = [ 4 | { 5 | path: '', 6 | component: UserComponent 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /client/app/module/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | import { Response } from '@angular/http' 3 | import { Observable } from 'rxjs/Observable' 4 | import 'rxjs/add/observable/throw' 5 | import 'rxjs/add/observable/fromPromise' 6 | 7 | 8 | import { SocketService } from '../../app.service' 9 | 10 | @Injectable() 11 | export class UserService { 12 | private socketUsersService 13 | 14 | constructor( 15 | private socketService: SocketService 16 | ) { 17 | this.socketUsersService = socketService.getService('users') 18 | } 19 | 20 | findUsers() { 21 | return Observable.fromPromise(this.socketUsersService.find()) 22 | .map((res: {data: any[]}) => res.data) 23 | .catch(this.handleError) 24 | } 25 | 26 | createUser(data) { 27 | return Observable.fromPromise(this.socketUsersService.create(data)) 28 | .catch(this.handleError) 29 | } 30 | 31 | updateUser(id, data) { 32 | return Observable.fromPromise(this.socketUsersService.update(id, data)) 33 | .catch(this.handleError) 34 | } 35 | 36 | 37 | removeUser(id) { 38 | return Observable.fromPromise(this.socketUsersService.remove(id)) 39 | .catch(this.handleError) 40 | } 41 | 42 | handleError(err: Response | any) { 43 | err = err instanceof Response ? err.json() : err.toString() 44 | console.error(err) 45 | return Observable.throw(err) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/app/shared/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 2 | ©2017 famn 3 | 4 | -------------------------------------------------------------------------------- /client/app/shared/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /client/app/shared/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core' 2 | import { NavigationService } from '../navigation.service' 3 | 4 | @Component({ 5 | selector: 'footer-cmp', 6 | templateUrl: 'footer.component.html', 7 | styleUrls: ['./footer.component.scss'] 8 | }) 9 | export class FooterComponent implements OnInit { 10 | constructor(private navigation: NavigationService) { } 11 | 12 | ngOnInit() { } 13 | } 14 | -------------------------------------------------------------------------------- /client/app/shared/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This barrel file provides the exports for the shared resources (services, components). 3 | */ 4 | 5 | // export * from './topnav/index' 6 | // export * from './sidebar/index' 7 | // export * from './+topnav/index' 8 | 9 | export * from './module.service' 10 | export * from './navigation.service' 11 | export * from './version.service' 12 | -------------------------------------------------------------------------------- /client/app/shared/material.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/core/theming/all-theme'; 2 | // Plus imports for other components in your app. 3 | 4 | // Include the base styles for Angular Material core. We include this here so that you only 5 | // have to load a single css file for Angular Material in your app. 6 | @include mat-core(); 7 | 8 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 9 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 10 | // hue. 11 | // $primary: mat-palette($mat-indigo); 12 | // $accent: mat-palette($mat-pink, A200, A100, A400); 13 | 14 | // The warn palette is optional (defaults to red). 15 | // $warn: mat-palette($mat-red); 16 | 17 | // Create the theme object (a Sass map containing all of the palettes). 18 | // $theme: mat-light-theme($primary, $accent, $warn); 19 | 20 | // Include theme styles for core and each component used in your app. 21 | // Alternatively, you can import and @include the theme mixins for each component 22 | // that you are using. 23 | // @include angular-material-theme($theme); 24 | 25 | 26 | $dark-primary: mat-palette($mat-blue-grey); 27 | $dark-accent: mat-palette($mat-amber, A200, A100, A400); 28 | $dark-warn: mat-palette($mat-deep-orange); 29 | 30 | $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn); 31 | 32 | @include angular-material-theme($dark-theme); 33 | -------------------------------------------------------------------------------- /client/app/shared/module.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | import { Http, Response } from '@angular/http' 3 | 4 | export interface ModuleMeta { 5 | id: string 6 | readme?: string 7 | name: string 8 | sources: string[] 9 | } 10 | 11 | @Injectable() 12 | export class ModuleService { 13 | public modules: any = null 14 | 15 | private _promise: Promise 16 | 17 | constructor(http: Http) { 18 | this._promise = new Promise((resolve) => { 19 | http.get('meta.json').subscribe((res: Response) => { 20 | this.modules = res.json() 21 | resolve() 22 | }) 23 | }) 24 | } 25 | 26 | getModules(): Promise { 27 | return this._promise.then(() => this.modules) 28 | } 29 | 30 | getModule(id: string): Promise { 31 | return this._promise.then(() => { 32 | let pick = null 33 | this.modules.forEach((m: ModuleMeta) => { 34 | if (m.id === id) { 35 | pick = m 36 | } 37 | }) 38 | return pick 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /client/app/shared/navigation.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | import { ModuleMeta } from './module.service' 3 | 4 | export interface INavigationLink { 5 | /** 6 | * Brief description of no more than a few words 7 | */ 8 | brief: string 9 | /** 10 | * Value to bind to routeLink. 11 | */ 12 | routeLink: string 13 | } 14 | 15 | @Injectable() 16 | export class NavigationService { 17 | public currentTitle: string = null 18 | public nextLink: INavigationLink = null 19 | public prevLink: INavigationLink = null 20 | 21 | moduleLink(mod: ModuleMeta): INavigationLink { 22 | return { brief: mod.name, routeLink: '/module/' + mod.id } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/app/shared/version.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | import { Http, Response } from '@angular/http' 3 | 4 | export interface IVersionMeta { 5 | version: string 6 | readme: string 7 | } 8 | 9 | @Injectable() 10 | export class VersionService { 11 | public meta: IVersionMeta = null 12 | 13 | private _promise: Promise 14 | 15 | constructor(http: Http) { 16 | this._promise = new Promise((resolve) => { 17 | http.get('version.json').subscribe((res: Response) => { 18 | this.meta = res.json() 19 | resolve() 20 | }) 21 | }) 22 | } 23 | 24 | getMeta(): Promise { 25 | return this._promise.then(() => { return this.meta }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/app/signup/signup.component.html: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /client/app/signup/signup.component.scss: -------------------------------------------------------------------------------- 1 | .signup { 2 | // position: fixed; 3 | // top: 0; 4 | // left: 0; 5 | // right: 0; 6 | // bottom: 0; 7 | // padding: 50px; 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | 12 | md-card { 13 | padding: 0; 14 | } 15 | 16 | md-card-content { 17 | padding: 16px; 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /client/app/signup/signup.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { Router } from '@angular/router' 3 | import { SignupService } from './signup.service' 4 | 5 | 6 | /** 7 | * This class represents the lazy loaded SignupComponent. 8 | */ 9 | @Component({ 10 | selector: 'signup-cmp', 11 | providers: [SignupService], 12 | templateUrl: 'signup.component.html', 13 | styleUrls: ['./signup.component.scss'] 14 | }) 15 | 16 | export class SignupComponent { 17 | public userModel = {} 18 | 19 | constructor(private router: Router, private signupService: SignupService) {} 20 | onSubmit() { 21 | this.signupService.signup(this.userModel).then(res => { 22 | if (res) this.router.navigate(['/']) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/app/signup/signup.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core' 2 | // import { Observable } from 'rxjs/Observable' 3 | import { SocketService } from '../app.service' 4 | 5 | 6 | @Injectable() 7 | export class SignupService { 8 | // public item$: Observable 9 | // private _observable 10 | private userService 11 | 12 | constructor(private userServiceService: SocketService) { 13 | // get featheres service 14 | this.userService = userServiceService.getService('users') 15 | 16 | // this.item$ is a public observable for components to subscribe 17 | // this.item$ = new Observable(observable => this._observable = observable) 18 | } 19 | 20 | signup(user: any) { 21 | return this.userService.create({ 22 | email: user.email, 23 | password: user.password 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/app/widgets/index.ts: -------------------------------------------------------------------------------- 1 | export * from './todo/index' 2 | export * from './stats/index' 3 | -------------------------------------------------------------------------------- /client/app/widgets/todo/index.ts: -------------------------------------------------------------------------------- 1 | export * from './todo' 2 | -------------------------------------------------------------------------------- /client/app/widgets/todo/todo.html: -------------------------------------------------------------------------------- 1 | 2 | To do Tasks 3 | 4 | 5 | 6 | {{name}} 7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /client/app/widgets/todo/todo.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'todo-cmp', 5 | templateUrl: './todo.html' 6 | }) 7 | 8 | export class TodoComponent implements OnInit { 9 | newName: string 10 | nameList: any = [ 11 | 'Meeting with Bryan.', 12 | 'Exercise at 6:pm with Yoga.', 13 | 'Lunch Lunch Lunch.', 14 | 'Her birthday at riverside.' 15 | ] 16 | addName(): boolean { 17 | this.nameList.push(this.newName) 18 | this.newName = '' 19 | return true 20 | } 21 | ngOnInit() { 22 | // let todoListWraper: any = $('.todo-list-wrap') 23 | // todoListWraper.perfectScrollbar({}) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/favicon.ico -------------------------------------------------------------------------------- /client/assets/icon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/android-icon-144x144.png -------------------------------------------------------------------------------- /client/assets/icon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/android-icon-192x192.png -------------------------------------------------------------------------------- /client/assets/icon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/android-icon-36x36.png -------------------------------------------------------------------------------- /client/assets/icon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/android-icon-48x48.png -------------------------------------------------------------------------------- /client/assets/icon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/android-icon-72x72.png -------------------------------------------------------------------------------- /client/assets/icon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/android-icon-96x96.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-114x114.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-120x120.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-144x144.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-152x152.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-180x180.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-57x57.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-60x60.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-72x72.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-76x76.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /client/assets/icon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/apple-icon.png -------------------------------------------------------------------------------- /client/assets/icon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /client/assets/icon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/favicon-16x16.png -------------------------------------------------------------------------------- /client/assets/icon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/favicon-32x32.png -------------------------------------------------------------------------------- /client/assets/icon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/favicon-96x96.png -------------------------------------------------------------------------------- /client/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/favicon.ico -------------------------------------------------------------------------------- /client/assets/icon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/ms-icon-144x144.png -------------------------------------------------------------------------------- /client/assets/icon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/ms-icon-150x150.png -------------------------------------------------------------------------------- /client/assets/icon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/ms-icon-310x310.png -------------------------------------------------------------------------------- /client/assets/icon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/assets/icon/ms-icon-70x70.png -------------------------------------------------------------------------------- /client/config/config.common.ts: -------------------------------------------------------------------------------- 1 | export const commonConfig = { 2 | port: 80, 3 | tokenKey: 'famn-jwt' 4 | } 5 | -------------------------------------------------------------------------------- /client/config/config.dev.ts: -------------------------------------------------------------------------------- 1 | import { commonConfig } from './config.common' 2 | 3 | export const devConfig = Object.assign({}, commonConfig, { 4 | port: '3030' 5 | }) 6 | -------------------------------------------------------------------------------- /client/config/config.prod.ts: -------------------------------------------------------------------------------- 1 | import { commonConfig } from './config.common' 2 | 3 | export const prodConfig = Object.assign({}, commonConfig, { 4 | port: '8080' 5 | }) 6 | -------------------------------------------------------------------------------- /client/config/config.ts: -------------------------------------------------------------------------------- 1 | import { devConfig } from './config.dev' 2 | import { prodConfig } from './config.prod' 3 | 4 | interface ClientConfig { 5 | port: string, 6 | tokenKey: string 7 | } 8 | 9 | let config: ClientConfig 10 | 11 | switch (ENV) { 12 | case 'production': 13 | case 'prod': 14 | config = prodConfig 15 | break 16 | case 'development': 17 | case 'dev': 18 | config = devConfig 19 | break 20 | default: 21 | break 22 | } 23 | 24 | export { config } 25 | -------------------------------------------------------------------------------- /client/config/empty.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NgProbeToken: {}, 3 | HmrState: function() {}, 4 | _createConditionalRootRenderer: function(rootRenderer, extraTokens, coreTokens) { 5 | return rootRenderer; 6 | }, 7 | __platform_browser_private__: {} 8 | }; 9 | -------------------------------------------------------------------------------- /client/config/helpers.ts: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | 4 | const EVENT = process.env.npm_lifecycle_event || '' 5 | 6 | // Helper functions 7 | const ROOT = path.resolve(__dirname, '../..') 8 | 9 | function hasProcessFlag(flag) { 10 | return process.argv.join('').indexOf(flag) > -1 11 | } 12 | 13 | function hasNpmFlag(flag) { 14 | return EVENT.includes(flag) 15 | } 16 | 17 | function isWebpackDevServer() { 18 | return process.argv[1] && !! (/webpack-dev-server$/.exec(process.argv[1])) 19 | } 20 | 21 | function root(args) { 22 | args = Array.prototype.slice.call(arguments, 0) 23 | return path.join.apply(path, [ROOT].concat(args)) 24 | } 25 | 26 | function checkNodeImport(context, request, cb) { 27 | if (!path.isAbsolute(request) && request.charAt(0) !== '.') { 28 | cb(null, 'commonjs ' + request) 29 | return 30 | } 31 | cb() 32 | } 33 | 34 | 35 | // exports.hasProcessFlag = hasProcessFlag 36 | // exports.isWebpackDevServer = isWebpackDevServer 37 | // exports.root = root 38 | // exports.checkNodeImport = checkNodeImport 39 | 40 | function getHost() { 41 | return location.host 42 | } 43 | 44 | export const helpers = { 45 | hasProcessFlag, 46 | hasNpmFlag, 47 | isWebpackDevServer, 48 | root, 49 | checkNodeImport, 50 | getHost 51 | } 52 | -------------------------------------------------------------------------------- /client/config/html-elements-plugin/index.ts: -------------------------------------------------------------------------------- 1 | 2 | function HtmlElementsPlugin(locations) { 3 | this.locations = locations; 4 | } 5 | 6 | HtmlElementsPlugin.prototype.apply = function(compiler) { 7 | var self = this; 8 | compiler.plugin('compilation', function(compilation) { 9 | compilation.options.htmlElements = compilation.options.htmlElements || {}; 10 | 11 | compilation.plugin('html-webpack-plugin-before-html-generation', function(htmlPluginData, callback) { 12 | const locations = self.locations; 13 | 14 | if (locations) { 15 | const publicPath = htmlPluginData.assets.publicPath; 16 | 17 | Object.getOwnPropertyNames(locations).forEach(function(loc) { 18 | compilation.options.htmlElements[loc] = getHtmlElementString(locations[loc], publicPath); 19 | }); 20 | } 21 | 22 | 23 | callback(null, htmlPluginData); 24 | }); 25 | }); 26 | 27 | }; 28 | 29 | const RE_ENDS_WITH_BS = /\/$/; 30 | 31 | /** 32 | * Create an HTML tag with attributes from a map. 33 | * 34 | * Example: 35 | * createTag('link', { rel: "manifest", href: "/assets/manifest.json" }) 36 | * // 37 | * @param tagName The name of the tag 38 | * @param attrMap A Map of attribute names (keys) and their values. 39 | * @param publicPath a path to add to eh start of static asset url 40 | * @returns {string} 41 | */ 42 | function createTag(tagName, attrMap, publicPath) { 43 | publicPath = publicPath || ''; 44 | 45 | // add trailing slash if we have a publicPath and it doesn't have one. 46 | if (publicPath && !RE_ENDS_WITH_BS.test(publicPath)) { 47 | publicPath += '/'; 48 | } 49 | 50 | const attributes = Object.getOwnPropertyNames(attrMap) 51 | .filter(function(name) { return name[0] !== '='; } ) 52 | .map(function(name) { 53 | var value = attrMap[name]; 54 | 55 | if (publicPath) { 56 | // check if we have explicit instruction, use it if so (e.g: =herf: false) 57 | // if no instruction, use public path if it's href attribute. 58 | const usePublicPath = attrMap.hasOwnProperty('=' + name) ? !!attrMap['=' + name] : name === 'href'; 59 | 60 | if (usePublicPath) { 61 | // remove a starting trailing slash if the value has one so we wont have // 62 | value = publicPath + (value[0] === '/' ? value.substr(1) : value); 63 | } 64 | } 65 | 66 | return name + '="' + value + '"'; 67 | }); 68 | 69 | return '<' + tagName + ' ' + attributes.join(' ') + '>'; 70 | } 71 | 72 | /** 73 | * Returns a string representing all html elements defined in a data source. 74 | * 75 | * Example: 76 | * 77 | * const ds = { 78 | * link: [ 79 | * { rel: "apple-touch-icon", sizes: "57x57", href: "/assets/icon/apple-icon-57x57.png" } 80 | * ], 81 | * meta: [ 82 | * { name: "msapplication-TileColor", content: "#00bcd4" } 83 | * ] 84 | * } 85 | * 86 | * getHeadTags(ds); 87 | * // "" 88 | * "" 89 | * 90 | * @returns {string} 91 | */ 92 | function getHtmlElementString(dataSource, publicPath) { 93 | return Object.getOwnPropertyNames(dataSource) 94 | .map(function(name) { 95 | if (Array.isArray(dataSource[name])) { 96 | return dataSource[name].map(function(attrs) { return createTag(name, attrs, publicPath); } ); 97 | } else { 98 | return [ createTag(name, dataSource[name], publicPath) ]; 99 | } 100 | }) 101 | .reduce(function(arr, curr) { 102 | return arr.concat(curr); 103 | }, []) 104 | .join('\n\t'); 105 | } 106 | module.exports = HtmlElementsPlugin; 107 | -------------------------------------------------------------------------------- /client/config/html-head-config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for head elements added during the creation of index.html. 3 | * 4 | * All href attributes are added the publicPath (if exists) by default. 5 | * You can explicitly hint to prefix a publicPath by setting a boolean value to a key that has 6 | * the same name as the attribute you want to operate on, but prefix with = 7 | * 8 | * Example: 9 | * { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 10 | * Will prefix the publicPath to content. 11 | * 12 | * { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png', '=href': false }, 13 | * Will not prefix the publicPath on href (href attributes are added by default 14 | * 15 | */ 16 | export const headTags = { 17 | link: [ 18 | /** tags for 'apple-touch-icon' (AKA Web Clips). **/ 19 | // { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png' }, 20 | // { rel: 'apple-touch-icon', sizes: '60x60', href: '/assets/icon/apple-icon-60x60.png' }, 21 | // { rel: 'apple-touch-icon', sizes: '72x72', href: '/assets/icon/apple-icon-72x72.png' }, 22 | // { rel: 'apple-touch-icon', sizes: '76x76', href: '/assets/icon/apple-icon-76x76.png' }, 23 | // { rel: 'apple-touch-icon', sizes: '114x114', href: '/assets/icon/apple-icon-114x114.png' }, 24 | // { rel: 'apple-touch-icon', sizes: '120x120', href: '/assets/icon/apple-icon-120x120.png' }, 25 | // { rel: 'apple-touch-icon', sizes: '144x144', href: '/assets/icon/apple-icon-144x144.png' }, 26 | // { rel: 'apple-touch-icon', sizes: '152x152', href: '/assets/icon/apple-icon-152x152.png' }, 27 | // { rel: 'apple-touch-icon', sizes: '180x180', href: '/assets/icon/apple-icon-180x180.png' }, 28 | 29 | /** tags for android web app icons **/ 30 | // { rel: 'icon', type: 'image/png', sizes: '192x192', href: '/assets/icon/android-icon-192x192.png' }, 31 | 32 | /** tags for favicons **/ 33 | // { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/assets/icon/favicon-32x32.png' }, 34 | // { rel: 'icon', type: 'image/png', sizes: '96x96', href: '/assets/icon/favicon-96x96.png' }, 35 | // { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/assets/icon/favicon-16x16.png' }, 36 | { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/assets/favicon.ico' }, 37 | 38 | /** tags for a Web App Manifest **/ 39 | { rel: 'manifest', href: '/assets/manifest.json' }, 40 | 41 | /** 42 | * tags for web font, stylesheet 43 | */ 44 | { 45 | rel: 'stylesheet', 46 | type: 'text/css', 47 | href: 'https://fonts.googleapis.com/css?family=Roboto:400,30', 48 | }, 49 | { 50 | rel: 'stylesheet', 51 | href: 'https://fonts.googleapis.com/icon?family=Material+Icons', 52 | } 53 | ], 54 | meta: [ 55 | { name: 'msapplication-TileColor', content: '#00bcd4' }, 56 | { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 57 | { name: 'theme-color', content: '#00bcd4' } 58 | ], 59 | }; 60 | -------------------------------------------------------------------------------- /client/config/resource-override.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/implustech/famn/a671782665d2da31213b328a0918608d27b542cc/client/config/resource-override.ts -------------------------------------------------------------------------------- /client/config/webpack.common.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: hjl 3 | */ 4 | 5 | const webpack = require('webpack') 6 | import { helpers } from './helpers' 7 | import { headTags } from './html-head-config' 8 | 9 | /* 10 | * Webpack Plugins 11 | */ 12 | // problem with copy-webpack-plugin 13 | const CopyWebpackPlugin = require('copy-webpack-plugin') 14 | const AssetsPlugin = require('assets-webpack-plugin') 15 | const HtmlWebpackPlugin = require('html-webpack-plugin') 16 | const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin 17 | const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin') 18 | const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin') 19 | const HtmlElementsPlugin = require('./html-elements-plugin') 20 | const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin') 21 | const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin') 22 | const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin') 23 | const ngcWebpack = require('ngc-webpack') 24 | 25 | 26 | /* 27 | * Webpack Constants 28 | */ 29 | const ENV = process.env.ENV = process.env.NODE_ENV = 'development' 30 | const HMR = helpers.hasProcessFlag('hot') 31 | const AOT = helpers.hasNpmFlag('aot') 32 | const METADATA = { 33 | title: 'FAMN Angular2 client-server boilerplate', 34 | baseUrl: '/', 35 | isDevServer: helpers.isWebpackDevServer() 36 | } 37 | 38 | /* 39 | * Webpack configuration 40 | * 41 | * See: http://webpack.github.io/docs/configuration.html#cli 42 | */ 43 | export function webpackCommonConfig(options) { 44 | let isProd = options.env === 'production' 45 | return { 46 | 47 | /* 48 | * Cache generated modules and chunks to improve performance for multiple incremental builds. 49 | * This is enabled by default in watch mode. 50 | * You can pass false to disable it. 51 | * 52 | * See: http://webpack.github.io/docs/configuration.html#cache 53 | */ 54 | //cache: false, 55 | 56 | /* 57 | * The entry point for the bundle 58 | * Our Angular.js app 59 | * 60 | * See: http://webpack.github.io/docs/configuration.html#entry 61 | */ 62 | entry: { 63 | 'polyfills': './client/polyfills.ts', 64 | 'main': AOT ? './client/main.aot.ts' : './client/main.ts' 65 | }, 66 | 67 | /* 68 | * Options affecting the resolving of modules. 69 | * 70 | * See: https://webpack.github.io/docs/configuration.html#externals 71 | */ 72 | externals: { 73 | jquery: 'jQuery' 74 | }, 75 | 76 | /* 77 | * Options affecting the resolving of modules. 78 | * 79 | * See: http://webpack.github.io/docs/configuration.html#resolve 80 | */ 81 | resolve: { 82 | 83 | /* 84 | * An array of extensions that should be used to resolve modules. 85 | * 86 | * See: http://webpack.github.io/docs/configuration.html#resolve-extensions 87 | */ 88 | extensions: ['.ts', '.js', '.json'], 89 | modules: [helpers.root('client/app'), helpers.root('node_modules')], 90 | }, 91 | 92 | /* 93 | * Options affecting the normal modules. 94 | * 95 | * See: http://webpack.github.io/docs/configuration.html#module 96 | */ 97 | module: { 98 | 99 | rules: [ 100 | 101 | /* 102 | * Typescript loader support for .ts 103 | * 104 | * Component Template/Style integration using `angular2-template-loader` 105 | * Angular 2 lazy loading (async routes) via `ng-router-loader` 106 | * 107 | * `ng-router-loader` expects vanilla JavaScript code, not TypeScript code. This is why the 108 | * order of the loader matter. 109 | * 110 | * See: https://github.com/s-panferov/awesome-typescript-loader 111 | * See: https://github.com/TheLarkInn/angular2-template-loader 112 | * See: https://github.com/shlomiassaf/ng-router-loader 113 | */ 114 | { 115 | test: /\.ts$/, 116 | use: [ 117 | { 118 | loader: '@angularclass/hmr-loader', 119 | options: { 120 | pretty: !isProd, 121 | prod: isProd 122 | } 123 | }, 124 | { // MAKE SURE TO CHAIN VANILLA JS CODE, I.E. TS COMPILATION OUTPUT. 125 | loader: 'ng-router-loader', 126 | options: { 127 | loader: 'async-import', 128 | genDir: 'aot', 129 | aot: AOT 130 | } 131 | }, 132 | { 133 | loader: 'awesome-typescript-loader', 134 | options: { 135 | configFileName: 'tsconfig.client.json' 136 | } 137 | }, 138 | { 139 | loader: 'angular2-template-loader' 140 | } 141 | ], 142 | exclude: [/\.(spec|e2e)\.ts$/] 143 | }, 144 | 145 | /* 146 | * Json loader support for *.json files. 147 | * 148 | * See: https://github.com/webpack/json-loader 149 | */ 150 | { 151 | test: /\.json$/, 152 | use: 'json-loader' 153 | }, 154 | 155 | /* 156 | * to string and css loader support for *.css files 157 | * Returns file content as string 158 | * 159 | */ 160 | { 161 | test: /\.css$/, 162 | // loaders: ['to-string-loader', 'css-loader'] 163 | use: ['style-loader', 'css-loader'] 164 | }, 165 | 166 | /* 167 | * to string and sass loader support for *.sass files 168 | * Returns file content as string 169 | * 170 | */ 171 | { 172 | test: /\.scss$/, 173 | exclude: /node_modules|global/, 174 | use: ['raw-loader', 'sass-loader'] 175 | }, 176 | { 177 | test: /global\.scss$/, 178 | use: ['style-loader', 'css-loader', 'sass-loader'] 179 | }, 180 | 181 | /* Raw loader support for *.html 182 | * Returns file content as string 183 | * 184 | * See: https://github.com/webpack/raw-loader 185 | */ 186 | { 187 | test: /\.html$/, 188 | use: 'raw-loader', 189 | exclude: [helpers.root('client/index.html')] 190 | }, 191 | 192 | /* File loader for supporting images, for example, in CSS files. 193 | */ 194 | { 195 | test: /\.(jpg|png|gif)$/, 196 | use: 'file-loader' 197 | }, 198 | { 199 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 200 | use: 'url?limit=10000&minetype=application/font-woff' 201 | }, 202 | { 203 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 204 | use: 'url?limit=10000&minetype=application/font-woff' 205 | }, 206 | { 207 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 208 | use: 'url?limit=10000&minetype=application/octet-stream' 209 | }, 210 | { 211 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 212 | use: 'file' 213 | }, 214 | { 215 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 216 | use: 'url?limit=10000&minetype=image/svg+xml' 217 | }, 218 | { 219 | test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, 220 | use: 'url?limit=10000' 221 | }, 222 | ] 223 | }, 224 | 225 | /* 226 | * Add additional plugins to the compiler. 227 | * 228 | * See: http://webpack.github.io/docs/configuration.html#plugins 229 | */ 230 | plugins: [ 231 | new AssetsPlugin({ 232 | path: helpers.root('dist'), 233 | filename: 'webpack-assets.json', 234 | prettyPrint: true 235 | }), 236 | 237 | /* 238 | * Plugin: ForkCheckerPlugin 239 | * Description: Do type checking in a separate process, so webpack don't need to wait. 240 | * 241 | * See: https://github.com/s-panferov/awesome-typescript-loader#forkchecker-boolean-defaultfalse 242 | */ 243 | new CheckerPlugin(), 244 | /* 245 | * Plugin: CommonsChunkPlugin 246 | * Description: Shares common code between the pages. 247 | * It identifies common modules and put them into a commons chunk. 248 | * 249 | * See: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin 250 | * See: https://github.com/webpack/docs/wiki/optimization#multi-page-app 251 | */ 252 | new CommonsChunkPlugin({ 253 | name: ['polyfills'], 254 | chunks: ['polyfills'] 255 | }), 256 | // This enables tree shaking of the vendor modules 257 | new CommonsChunkPlugin({ 258 | name: 'vendor', 259 | chunks: ['main'], 260 | minChunks: module => /node_modules/.test(module.resource) 261 | }), 262 | // Specify the correct order the scripts will be injected in 263 | new CommonsChunkPlugin({ 264 | name: ['polyfills', 'vendor'].reverse() 265 | }), 266 | 267 | /** 268 | * Plugin: ContextReplacementPlugin 269 | * Description: Provides context to Angular's use of System.import 270 | * 271 | * See: https://webpack.github.io/docs/list-of-plugins.html#contextreplacementplugin 272 | * See: https://github.com/angular/angular/issues/11580 273 | */ 274 | new ContextReplacementPlugin( 275 | // The (\\|\/) piece accounts for path separators in *nix and Windows 276 | /angular(\\|\/)core(\\|\/)src(\\|\/)linker/, 277 | helpers.root('client') // location of your src 278 | ), 279 | 280 | new webpack.ProvidePlugin({ 281 | $: 'jquery' 282 | }), 283 | 284 | /* 285 | * Plugin: CopyWebpackPlugin 286 | * Description: Copy files and directories in webpack. 287 | * 288 | * Copies project static assets. 289 | * 290 | * See: https://www.npmjs.com/package/copy-webpack-plugin 291 | */ 292 | new CopyWebpackPlugin([{ 293 | from: 'client/assets', 294 | to: 'assets' 295 | }, { 296 | from: 'client/assets/favicon.ico', 297 | to: 'favicon.ico' 298 | }, { 299 | from: 'client/app/meta.json', 300 | to: 'meta.json' 301 | }]), 302 | 303 | /* 304 | * Plugin: HtmlWebpackPlugin 305 | * Description: Simplifies creation of HTML files to serve your webpack bundles. 306 | * This is especially useful for webpack bundles that include a hash in the filename 307 | * which changes every compilation. 308 | * 309 | * See: https://github.com/ampedandwired/html-webpack-plugin 310 | */ 311 | new HtmlWebpackPlugin({ 312 | template: 'client/index.html', 313 | title: METADATA.title, 314 | chunksSortMode: 'dependency', 315 | metadata: METADATA, 316 | inject: 'head' 317 | }), 318 | 319 | 320 | /* 321 | * Plugin: ScriptExtHtmlWebpackPlugin 322 | * Description: Enhances html-webpack-plugin functionality 323 | * with different deployment options for your scripts including: 324 | * 325 | * See: https://github.com/numical/script-ext-html-webpack-plugin 326 | */ 327 | new ScriptExtHtmlWebpackPlugin({ 328 | defaultAttribute: 'defer' 329 | }), 330 | 331 | 332 | 333 | /* 334 | * Plugin: HtmlHeadConfigPlugin 335 | * Description: Generate html tags based on javascript maps. 336 | * 337 | * If a publicPath is set in the webpack output configuration, it will be automatically added to 338 | * href attributes, you can disable that by adding a "=href": false property. 339 | * You can also enable it to other attribute by settings "=attName": true. 340 | * 341 | * The configuration supplied is map between a location (key) and an element definition object (value) 342 | * The location (key) is then exported to the template under then htmlElements property in webpack configuration. 343 | * 344 | * Example: 345 | * Adding this plugin configuration 346 | * new HtmlElementsPlugin({ 347 | * headTags: { ... } 348 | * }) 349 | * 350 | * Means we can use it in the template like this: 351 | * <%= webpackConfig.htmlElements.headTags %> 352 | * 353 | * Dependencies: HtmlWebpackPlugin 354 | */ 355 | new HtmlElementsPlugin({ 356 | headTags: headTags 357 | }), 358 | 359 | /** 360 | * Plugin LoaderOptionsPlugin (experimental) 361 | * 362 | * See: https://gist.github.com/sokra/27b24881210b56bbaff7 363 | */ 364 | new LoaderOptionsPlugin({}), 365 | 366 | 367 | // Fix Angular 2 368 | new NormalModuleReplacementPlugin( 369 | /facade(\\|\/)async/, 370 | helpers.root('node_modules/@angular/core/src/facade/async.js') 371 | ), 372 | new NormalModuleReplacementPlugin( 373 | /facade(\\|\/)collection/, 374 | helpers.root('node_modules/@angular/core/src/facade/collection.js') 375 | ), 376 | new NormalModuleReplacementPlugin( 377 | /facade(\\|\/)errors/, 378 | helpers.root('node_modules/@angular/core/src/facade/errors.js') 379 | ), 380 | new NormalModuleReplacementPlugin( 381 | /facade(\\|\/)lang/, 382 | helpers.root('node_modules/@angular/core/src/facade/lang.js') 383 | ), 384 | new NormalModuleReplacementPlugin( 385 | /facade(\\|\/)math/, 386 | helpers.root('node_modules/@angular/core/src/facade/math.js') 387 | ), 388 | 389 | new ngcWebpack.NgcWebpackPlugin({ 390 | disabled: !AOT, 391 | tsConfig: helpers.root('tsconfig.webpack.json'), 392 | resourceOverride: helpers.root('./client/config/resource-override.ts') 393 | }) 394 | 395 | ], 396 | 397 | 398 | /* 399 | * Include polyfills or mocks for various node stuff 400 | * Description: Node configuration 401 | * 402 | * See: https://webpack.github.io/docs/configuration.html#node 403 | */ 404 | node: { 405 | global: true, 406 | crypto: 'empty', 407 | process: true, 408 | module: false, 409 | clearImmediate: false, 410 | setImmediate: false 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /client/config/webpack.dev.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: hjl 3 | */ 4 | 5 | const webpack = require('webpack') 6 | const webpackMerge = require('webpack-merge') // used to merge webpack configs 7 | const webpackMergeDll = webpackMerge.strategy({ plugins: 'replace' }) 8 | import { helpers } from './helpers' 9 | import { webpackCommonConfig } from './webpack.common' // the settings that are common to prod and dev 10 | 11 | /** 12 | * Webpack Plugins 13 | */ 14 | const DefinePlugin = require('webpack/lib/DefinePlugin') 15 | const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin') 16 | const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') 17 | const NamedModulesPlugin = require('webpack/lib/NamedModulesPlugin') 18 | const DllBundlesPlugin = require('webpack-dll-bundles-plugin').DllBundlesPlugin 19 | 20 | 21 | 22 | 23 | 24 | /** 25 | * Webpack Constants 26 | */ 27 | const ENV = process.env.ENV = process.env.NODE_ENV = 'development' 28 | const HOST = process.env.HOST || 'localhost' 29 | const PORT = process.env.PORT || 3030 30 | const HMR = process.env.HMR 31 | // const METADATA = webpackMerge(commonConfig({ env: ENV }).metadata, { 32 | const METADATA = webpackMerge({}, { 33 | host: HOST, 34 | port: PORT, 35 | ENV: ENV, 36 | HMR: HMR 37 | }) 38 | 39 | 40 | 41 | /** 42 | * Webpack configuration 43 | * 44 | * See: http://webpack.github.io/docs/configuration.html#cli 45 | */ 46 | 47 | 48 | export default webpackMerge(webpackCommonConfig({ env: ENV }), { 49 | 50 | 51 | /** 52 | * Merged metadata from webpack.common.js for index.html 53 | * 54 | * See: (custom attribute) 55 | */ 56 | // metadata: METADATA, 57 | 58 | /* 59 | * The entry point for the bundle 60 | * Our Angular.js app 61 | * 62 | * See: http://webpack.github.io/docs/configuration.html#entry 63 | */ 64 | entry: { 65 | // 'polyfills': ['./client/polyfills.ts'].concat(HMR ? ['webpack-hot-middleware/client'] : []), 66 | 'polyfills': ['./client/polyfills.ts'], 67 | 'main': ['./client/main.ts'].concat(HMR ? ['webpack-hot-middleware/client'] : []), 68 | }, 69 | 70 | 71 | 72 | /** 73 | * Developer tool to enhance debugging 74 | * 75 | * See: http://webpack.github.io/docs/configuration.html#devtool 76 | * See: https://github.com/webpack/docs/wiki/build-performance#sourcemaps 77 | */ 78 | devtool: 'cheap-module-source-map', 79 | 80 | /** 81 | * Options affecting the output of the compilation. 82 | * 83 | * See: http://webpack.github.io/docs/configuration.html#output 84 | */ 85 | output: { 86 | 87 | /** 88 | * The output directory as absolute path (required). 89 | * 90 | * See: http://webpack.github.io/docs/configuration.html#output-path 91 | */ 92 | path: helpers.root('dist/client'), 93 | 94 | /** 95 | * Specifies the name of each output file on disk. 96 | * IMPORTANT: You must not specify an absolute path here! 97 | * 98 | * See: http://webpack.github.io/docs/configuration.html#output-filename 99 | */ 100 | filename: '[name].bundle.js', 101 | 102 | /** 103 | * The filename of the SourceMaps for the JavaScript files. 104 | * They are inside the output.path directory. 105 | * 106 | * See: http://webpack.github.io/docs/configuration.html#output-sourcemapfilename 107 | */ 108 | sourceMapFilename: '[file].map', 109 | 110 | /** The filename of non-entry chunks as relative path 111 | * inside the output.path directory. 112 | * 113 | * See: http://webpack.github.io/docs/configuration.html#output-chunkfilename 114 | */ 115 | chunkFilename: '[id].chunk.js', 116 | 117 | library: 'ac_[name]', 118 | libraryTarget: 'var', 119 | }, 120 | 121 | plugins: [ 122 | 123 | /** 124 | * Plugin: DefinePlugin 125 | * Description: Define free variables. 126 | * Useful for having development builds with debug logging or adding global constants. 127 | * 128 | * Environment helpers 129 | * 130 | * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin 131 | */ 132 | // NOTE: when adding more properties, make sure you include them in custom-typings.d.ts 133 | new DefinePlugin({ 134 | 'ENV': JSON.stringify(METADATA.ENV), 135 | 'HMR': METADATA.HMR, 136 | 'process.env': { 137 | 'ENV': JSON.stringify(METADATA.ENV), 138 | 'NODE_ENV': JSON.stringify(METADATA.ENV), 139 | 'HMR': METADATA.HMR, 140 | } 141 | }), 142 | 143 | new DllBundlesPlugin({ 144 | bundles: { 145 | polyfills: [ 146 | 'core-js', 147 | { 148 | name: 'zone.js', 149 | path: 'zone.js/dist/zone.js' 150 | }, 151 | { 152 | name: 'zone.js', 153 | path: 'zone.js/dist/long-stack-trace-zone.js' 154 | } 155 | ], 156 | vendor: [ 157 | '@angular/platform-browser', 158 | '@angular/platform-browser-dynamic', 159 | '@angular/core', 160 | '@angular/common', 161 | '@angular/forms', 162 | '@angular/http', 163 | '@angular/router', 164 | '@angularclass/hmr', 165 | 'socket.io-client', 166 | 'feathers', 167 | 'feathers-socketio', 168 | 'feathers-hooks', 169 | 'feathers-authentication-client', 170 | 'rxjs', 171 | ] 172 | }, 173 | dllDir: helpers.root('dll'), 174 | webpackConfig: webpackMergeDll(webpackCommonConfig({ env: ENV }), { 175 | devtool: 'cheap-module-source-map', 176 | plugins: [] 177 | }) 178 | }), 179 | 180 | /** 181 | * Plugin: AddAssetHtmlPlugin 182 | * Description: Adds the given JS or CSS file to the files 183 | * Webpack knows about, and put it into the list of assets 184 | * html-webpack-plugin injects into the generated html. 185 | * 186 | * See: https://github.com/SimenB/add-asset-html-webpack-plugin 187 | */ 188 | new AddAssetHtmlPlugin([ 189 | { filepath: helpers.root(`dll/${DllBundlesPlugin.resolveFile('polyfills')}`) }, 190 | { filepath: helpers.root(`dll/${DllBundlesPlugin.resolveFile('vendor')}`) } 191 | ]), 192 | 193 | 194 | /** 195 | * Plugin LoaderOptionsPlugin (experimental) 196 | * 197 | * See: https://gist.github.com/sokra/27b24881210b56bbaff7 198 | */ 199 | new LoaderOptionsPlugin({ 200 | debug: true, 201 | options: { 202 | } 203 | }), 204 | 205 | 206 | // Used with webpack-hot-middllware 207 | new webpack.HotModuleReplacementPlugin(), 208 | new webpack.NoEmitOnErrorsPlugin() 209 | 210 | ], 211 | 212 | /** 213 | * Webpack Development Server configuration 214 | * Description: The webpack-dev-server is a little node.js Express server. 215 | * The server emits information about the compilation state to the client, 216 | * which reacts to those events. 217 | * 218 | * See: https://webpack.github.io/docs/webpack-dev-server.html 219 | */ 220 | devServer: { 221 | port: METADATA.port, 222 | host: METADATA.host, 223 | historyApiFallback: true, 224 | watchOptions: { 225 | aggregateTimeout: 300, 226 | poll: 1000 227 | }, 228 | outputPath: helpers.root('dist/client') 229 | }, 230 | 231 | /* 232 | * Include polyfills or mocks for various node stuff 233 | * Description: Node configuration 234 | * 235 | * See: https://webpack.github.io/docs/configuration.html#node 236 | */ 237 | node: { 238 | global: true, 239 | crypto: 'empty', 240 | process: true, 241 | module: false, 242 | clearImmediate: false, 243 | setImmediate: false 244 | } 245 | }) 246 | -------------------------------------------------------------------------------- /client/config/webpack.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: hjl 3 | */ 4 | 5 | import { helpers } from './helpers' 6 | const webpackMerge = require('webpack-merge') // used to merge webpack configs 7 | import { webpackCommonConfig } from './webpack.common' // the settings that are common to prod and dev 8 | 9 | /** 10 | * Webpack Plugins 11 | */ 12 | const DefinePlugin = require('webpack/lib/DefinePlugin') 13 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 14 | const IgnorePlugin = require('webpack/lib/IgnorePlugin') 15 | const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin') 16 | const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin') 17 | const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin') 18 | const ProvidePlugin = require('webpack/lib/ProvidePlugin') 19 | const OptimizeJsPlugin = require('optimize-js-plugin') 20 | 21 | /** 22 | * Webpack Constants 23 | */ 24 | const ENV = process.env.ENV = process.env.NODE_ENV = 'production' 25 | const HOST = process.env.HOST || 'localhost' 26 | const PORT = process.env.PORT || 8080 27 | // const METADATA = webpackMerge(commonConfig({ env: ENV }).metadata, { 28 | const METADATA = webpackMerge({}, { 29 | host: HOST, 30 | port: PORT, 31 | ENV: ENV, 32 | HMR: undefined 33 | }) 34 | 35 | /** 36 | * Webpack configuration 37 | * 38 | * See: http://webpack.github.io/docs/configuration.html#cli 39 | */ 40 | 41 | export default webpackMerge(webpackCommonConfig({ env: ENV }), { 42 | 43 | /** 44 | * Developer tool to enhance debugging 45 | * See: http://webpack.github.io/docs/configuration.html#devtool 46 | * See: https://github.com/webpack/docs/wiki/build-performance#sourcemaps 47 | */ 48 | devtool: 'source-map', 49 | 50 | /** 51 | * Options affecting the output of the compilation. 52 | * 53 | * See: http://webpack.github.io/docs/configuration.html#output 54 | */ 55 | output: { 56 | 57 | /** 58 | * The output directory as absolute path (required). 59 | * 60 | * See: http://webpack.github.io/docs/configuration.html#output-path 61 | */ 62 | path: helpers.root('dist/client'), 63 | 64 | /** 65 | * Specifies the name of each output file on disk. 66 | * IMPORTANT: You must not specify an absolute path here! 67 | * 68 | * See: http://webpack.github.io/docs/configuration.html#output-filename 69 | */ 70 | filename: '[name].[chunkhash].bundle.js', 71 | 72 | /** 73 | * The filename of the SourceMaps for the JavaScript files. 74 | * They are inside the output.path directory. 75 | * 76 | * See: http://webpack.github.io/docs/configuration.html#output-sourcemapfilename 77 | */ 78 | sourceMapFilename: '[name].[chunkhash].bundle.map', 79 | 80 | /** The filename of non-entry chunks as relative path 81 | * inside the output.path directory. 82 | * 83 | * See: http://webpack.github.io/docs/configuration.html#output-chunkfilename 84 | */ 85 | chunkFilename: '[id].[chunkhash].chunk.js' 86 | }, 87 | 88 | plugins: [ 89 | 90 | /** 91 | * Webpack plugin to optimize a JavaScript file for faster initial load 92 | * by wrapping eagerly-invoked functions. 93 | * 94 | * See: https://github.com/vigneshshanmugam/optimize-js-plugin 95 | */ 96 | 97 | new OptimizeJsPlugin({ 98 | sourceMap: false 99 | }), 100 | 101 | new ExtractTextPlugin('[name].[contenthash].css'), 102 | 103 | 104 | /** 105 | * Plugin: DefinePlugin 106 | * Description: Define free variables. 107 | * Useful for having development builds with debug logging or adding global constants. 108 | * 109 | * Environment helpers 110 | * 111 | * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin 112 | */ 113 | // NOTE: when adding more properties, make sure you include them in custom-typings.d.ts 114 | new DefinePlugin({ 115 | 'ENV': JSON.stringify(METADATA.ENV), 116 | 'HMR': METADATA.HMR, 117 | 'process.env': { 118 | 'ENV': JSON.stringify(METADATA.ENV), 119 | 'NODE_ENV': JSON.stringify(METADATA.ENV), 120 | 'HMR': METADATA.HMR, 121 | } 122 | }), 123 | 124 | 125 | /** 126 | * Plugin: UglifyJsPlugin 127 | * Description: Minimize all JavaScript output of chunks. 128 | * Loaders are switched into minimizing mode. 129 | * 130 | * See: https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin 131 | */ 132 | // NOTE: To debug prod builds uncomment //debug lines and comment //prod lines 133 | new UglifyJsPlugin({ 134 | beautify: false, //prod 135 | output: { 136 | comments: false 137 | }, //prod 138 | mangle: { 139 | screw_ie8: true 140 | }, //prod 141 | compress: { 142 | screw_ie8: true, 143 | warnings: false, 144 | conditionals: true, 145 | unused: true, 146 | comparisons: true, 147 | sequences: true, 148 | dead_code: true, 149 | evaluate: true, 150 | if_return: true, 151 | join_vars: true, 152 | negate_iife: false // we need this for lazy v8 153 | }, 154 | }), 155 | 156 | 157 | 158 | /** 159 | * Plugin: NormalModuleReplacementPlugin 160 | * Description: Replace resources that matches resourceRegExp with newResource 161 | * 162 | * See: http://webpack.github.io/docs/list-of-plugins.html#normalmodulereplacementplugin 163 | */ 164 | 165 | new NormalModuleReplacementPlugin( 166 | /angular2-hmr/, 167 | helpers.root('client/config/empty.ts') 168 | ), 169 | new NormalModuleReplacementPlugin( 170 | /zone\.js(\\|\/)dist(\\|\/)long-stack-trace-zone/, 171 | helpers.root('client/config/empty.ts') 172 | ), 173 | 174 | 175 | /** 176 | * Plugin LoaderOptionsPlugin (experimental) 177 | * 178 | * See: https://gist.github.com/sokra/27b24881210b56bbaff7 179 | */ 180 | new LoaderOptionsPlugin({ 181 | minimize: true, 182 | debug: false, 183 | options: { 184 | 185 | /** 186 | * Html loader advanced options 187 | * 188 | * See: https://github.com/webpack/html-loader#advanced-options 189 | */ 190 | // TODO: Need to workaround Angular 2's html syntax => #id [bind] (event) *ngFor 191 | htmlLoader: { 192 | minimize: true, 193 | removeAttributeQuotes: false, 194 | caseSensitive: true, 195 | customAttrSurround: [ 196 | [/#/, /(?:)/], 197 | [/\*/, /(?:)/], 198 | [/\[?\(?/, /(?:)/] 199 | ], 200 | customAttrAssign: [/\)?\]?=/] 201 | }, 202 | 203 | } 204 | }), 205 | ], 206 | 207 | /* 208 | * Include polyfills or mocks for various node stuff 209 | * Description: Node configuration 210 | * 211 | * See: https://webpack.github.io/docs/configuration.html#node 212 | */ 213 | node: { 214 | global: true, 215 | crypto: 'empty', 216 | process: false, 217 | module: false, 218 | clearImmediate: false, 219 | setImmediate: false 220 | } 221 | }) 222 | -------------------------------------------------------------------------------- /client/custom-typings.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Custom Type Definitions 3 | * When including 3rd party modules you also need to include the type definition for the module 4 | * if they don't provide one within the module. You can try to install it with @types 5 | 6 | npm install @types/node 7 | npm install @types/lodash 8 | 9 | * If you can't find the type definition in the registry we can make an ambient/global definition in 10 | * this file for now. For example 11 | 12 | declare module "my-module" { 13 | export function doesSomething(value: string): string; 14 | } 15 | 16 | * If you are using a CommonJS module that is using module.exports then you will have to write your 17 | * types using export = yourObjectOrFunction with a namespace above it 18 | * notice how we have to create a namespace that is equal to the function we're assigning the export to 19 | 20 | declare module "jwt-decode" { 21 | function jwtDecode(token: string): any; 22 | namespace jwtDecode {} 23 | export = jwtDecode; 24 | } 25 | 26 | * 27 | * If you're prototying and you will fix the types later you can also declare it as type any 28 | * 29 | 30 | declare var assert: any; 31 | declare var _: any; 32 | declare var $: any; 33 | 34 | * 35 | * If you're importing a module that uses Node.js modules which are CommonJS you need to import as 36 | * in the files such as main.browser.ts or any file within app/ 37 | * 38 | 39 | import * as _ from 'lodash' 40 | 41 | * You can include your type definitions in this file until you create one for the @types 42 | * 43 | */ 44 | 45 | declare var $: any 46 | 47 | // support NodeJS modules without type definitions 48 | declare module "*" 49 | 50 | // Extra variables that live on Global that will be replaced by webpack DefinePlugin 51 | declare var ENV: string 52 | declare var HMR: boolean 53 | declare var System: SystemJS 54 | 55 | interface SystemJS { 56 | import: (path?: string) => Promise 57 | } 58 | 59 | interface GlobalEnvironment { 60 | ENV; 61 | HMR; 62 | SystemJS: SystemJS; 63 | System: SystemJS; 64 | } 65 | 66 | interface Es6PromiseLoader { 67 | (id: string): (exportName?: string) => Promise; 68 | } 69 | 70 | type FactoryEs6PromiseLoader = () => Es6PromiseLoader; 71 | type FactoryPromise = () => Promise; 72 | 73 | type AsyncRoutes = { 74 | [component: string]: Es6PromiseLoader | 75 | Function | 76 | FactoryEs6PromiseLoader | 77 | FactoryPromise 78 | }; 79 | 80 | 81 | type IdleCallbacks = Es6PromiseLoader | 82 | Function | 83 | FactoryEs6PromiseLoader | 84 | FactoryPromise ; 85 | 86 | interface WebpackModule { 87 | hot: { 88 | data?: any, 89 | idle: any, 90 | accept(dependencies?: string | string[], callback?: (updatedDependencies?: any) => void): void; 91 | decline(deps?: any | string | string[]): void; 92 | dispose(callback?: (data?: any) => void): void; 93 | addDisposeHandler(callback?: (data?: any) => void): void; 94 | removeDisposeHandler(callback?: (data?: any) => void): void; 95 | check(autoApply?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void; 96 | apply(options?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void; 97 | status(callback?: (status?: string) => void): void | string; 98 | removeStatusHandler(callback?: (status?: string) => void): void; 99 | }; 100 | } 101 | 102 | 103 | interface WebpackRequire { 104 | (id: string): any; 105 | (paths: string[], callback: (...modules: any[]) => void): void; 106 | ensure(ids: string[], callback: (req: WebpackRequire) => void, chunkName?: string): void; 107 | context(directory: string, useSubDirectories?: boolean, regExp?: RegExp): WebpackContext; 108 | } 109 | 110 | interface WebpackContext extends WebpackRequire { 111 | keys(): string[]; 112 | } 113 | 114 | interface ErrorStackTraceLimit { 115 | stackTraceLimit: number; 116 | } 117 | 118 | 119 | // Extend typings 120 | interface NodeRequire extends WebpackRequire {} 121 | interface ErrorConstructor extends ErrorStackTraceLimit {} 122 | interface NodeRequireFunction extends Es6PromiseLoader {} 123 | interface NodeModule extends WebpackModule {} 124 | interface Global extends GlobalEnvironment {} 125 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% if (webpackConfig.htmlElements.headTags) { %> 19 | 20 | <%= webpackConfig.htmlElements.headTags %> 21 | <% } %> 22 | 23 | 24 | 25 | 36 | 37 | 38 | 39 | 40 |
41 |
42 | 43 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /client/main.aot.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Angular bootstraping 3 | */ 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' 5 | import { decorateModuleRef } from './app/env' 6 | /* 7 | * App Module 8 | * our top level module that holds all of our components 9 | */ 10 | import { AppModuleNgFactory } from '../aot/src/app/app.module.ngfactory' 11 | 12 | 13 | /* 14 | * Bootstrap our Angular app with a top level NgModule 15 | */ 16 | export function main(): Promise { 17 | return platformBrowserDynamic() 18 | .bootstrapModule(AppModuleNgFactory) 19 | .then(decorateModuleRef) 20 | .catch(err => console.error(err)) 21 | } 22 | 23 | export function bootstrapDomReady() { 24 | document.addEventListener('DOMContentLoaded', main) 25 | } 26 | 27 | bootstrapDomReady() 28 | -------------------------------------------------------------------------------- /client/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Angular bootstraping 3 | */ 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' 5 | import { decorateModuleRef } from './app/env' 6 | import { bootloader } from '@angularclass/hmr' 7 | /* 8 | * App Module 9 | * our top level module that holds all of our components 10 | */ 11 | import { AppModule } from './app' 12 | 13 | /* 14 | * Bootstrap our Angular app with a top level NgModule 15 | */ 16 | export function main(): Promise { 17 | return platformBrowserDynamic() 18 | .bootstrapModule(AppModule) 19 | .then(decorateModuleRef) 20 | .catch(err => console.error(err)) 21 | } 22 | 23 | // needed for hmr 24 | // in prod this is replace for document ready 25 | if (process.env.ENV === 'development') bootloader(main) 26 | else platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err)) 27 | 28 | -------------------------------------------------------------------------------- /client/polyfills.ts: -------------------------------------------------------------------------------- 1 | // TODO(gdi2290): switch to DLLs 2 | 3 | // Polyfills 4 | 5 | // import 'ie-shim'; // Internet Explorer 9 support 6 | 7 | 8 | // import 'core-js/es6'; 9 | // Added parts of es6 which are necessary for your project or your browser support requirements. 10 | import 'core-js/es6/symbol' 11 | import 'core-js/es6/object' 12 | import 'core-js/es6/function' 13 | import 'core-js/es6/parse-int' 14 | import 'core-js/es6/parse-float' 15 | import 'core-js/es6/number' 16 | import 'core-js/es6/math' 17 | import 'core-js/es6/string' 18 | import 'core-js/es6/date' 19 | import 'core-js/es6/array' 20 | import 'core-js/es6/regexp' 21 | import 'core-js/es6/map' 22 | import 'core-js/es6/set' 23 | import 'core-js/es6/weak-map' 24 | import 'core-js/es6/weak-set' 25 | import 'core-js/es6/typed' 26 | import 'core-js/es6/reflect' 27 | // see issue https://github.com/AngularClass/angular2-webpack-starter/issues/709 28 | // import 'core-js/es6/promise' 29 | 30 | import 'core-js/es7/reflect' 31 | import 'zone.js/dist/zone' 32 | 33 | 34 | if ('production' === ENV) { // Production 35 | 36 | 37 | } else { 38 | // Development 39 | 40 | Error.stackTraceLimit = Infinity 41 | 42 | require('zone.js/dist/long-stack-trace-zone') 43 | 44 | } 45 | -------------------------------------------------------------------------------- /client/reducer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './message' 2 | -------------------------------------------------------------------------------- /client/reducer/message.ts: -------------------------------------------------------------------------------- 1 | import { ActionReducer, Action } from '@ngrx/store' 2 | 3 | export const MESSAGE_INIT = 'MESSAGE_INIT' 4 | export const MESSAGE_UPDATE = 'MESSAGE_UPDATE' 5 | 6 | export const message: ActionReducer = (state = [], action: Action) => { 7 | switch (action.type) { 8 | case MESSAGE_INIT: 9 | return action.payload 10 | case MESSAGE_UPDATE: 11 | return [ 12 | ...state, 13 | ...action.payload 14 | ] 15 | default: 16 | return [...state] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docker/dev.dockerfile: -------------------------------------------------------------------------------- 1 | FROM elaijuh/node 2 | ENV PROJECT_NAME famn 3 | MAINTAINER hjl 4 | 5 | RUN mkdir /famn 6 | WORKDIR /famn 7 | 8 | COPY package.json yarn.lock /famn/ 9 | RUN yarn --ignore-optional && yarn cache clean 10 | 11 | EXPOSE 3030 12 | 13 | CMD yarn run start:hmr 14 | -------------------------------------------------------------------------------- /docker/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | famn: 4 | build: 5 | context: .. 6 | dockerfile: ./docker/dev.dockerfile 7 | environment: 8 | - NODE_ENV=development 9 | - NODE_CONFIG_DIR=./app/config 10 | ports: 11 | - "3030:3030" 12 | volumes: 13 | - ..:/famn/ 14 | - /famn/node_modules 15 | links: 16 | - mongo 17 | mongo: 18 | image: mongo:3.4 19 | ports: 20 | - "27017:27017" # for host connect 21 | volumes: 22 | - ~/docker/mongodb/log:/var/log/mongodb 23 | - ~/docker/data/db:/data/db 24 | -------------------------------------------------------------------------------- /docker/docker-compose.prod.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | famn: 4 | build: 5 | context: .. 6 | dockerfile: ./docker/prod.dockerfile 7 | environment: 8 | NODE_ENV: production 9 | NODE_CONFIG_DIR: ./app/config 10 | ports: 11 | - "8080:8080" 12 | volumes: 13 | - ~/docker/famn/log:/var/log/famn 14 | links: 15 | - mongo 16 | mongo: 17 | image: mongo:3.4 18 | ports: 19 | - "27017:27017" # for host connect 20 | volumes: 21 | - ~/docker/mongodb/log:/var/log/mongodb 22 | - ~/docker/data/db:/data/db 23 | -------------------------------------------------------------------------------- /docker/prod.dockerfile: -------------------------------------------------------------------------------- 1 | FROM elaijuh/node 2 | ENV PROJECT_NAME famn 3 | MAINTAINER hjl 4 | 5 | RUN mkdir /famn 6 | WORKDIR /famn 7 | 8 | COPY package.json yarn.lock /famn/ 9 | RUN yarn --ignore-optional && yarn cache clean 10 | 11 | COPY . /famn 12 | 13 | EXPOSE 8080 14 | 15 | RUN yarn run build:client:prod 16 | RUN yarn run build:server:prod 17 | CMD sleep 5s && yarn run start:prod 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "famn", 3 | "version": "0.1.3", 4 | "description": "famn - angular2 application seed", 5 | "scripts": { 6 | "rimraf": "rimraf", 7 | "tslint": "tslint", 8 | "webpack": "webpack", 9 | "webpack-dev-server": "webpack-dev-server", 10 | "clean": "yarn cache clean && yarn run rimraf -- node_modules dist dll compiled", 11 | "clean:dist": "yarn run rimraf -- dist", 12 | "clean:dll": "yarn run rimraf -- dll", 13 | "clean:aot": "yarn run rimraf -- aot", 14 | "start": "yarn run start:hmr", 15 | "start:hmr": "NODE_ENV=development HMR=true nodemon --watch 'app/**/*.ts' --exec 'ts-node' server.ts", 16 | "start:prod": "NODE_ENV=production node ./dist/server/server.bundle.js", 17 | "build": "yarn run build:client:dev", 18 | "build:server": "yarn run build:server:prod", 19 | "prebuild:client:dev": "yarn run clean:dist", 20 | "prebuild:client:prod": "yarn run clean:dist", 21 | "prebuild:client:aot:prod": "yarn run clean:dist && yarn run clean:aot", 22 | "build:client:dev": "webpack --config ./client/config/webpack.dev.ts --progress --profile", 23 | "build:client:prod": "webpack --config ./client/config/webpack.prod.ts --progress --profile --bail", 24 | "build:client:aot:prod": "webpack --config ./client/config/webpack.prod.ts --progress --profile --bail", 25 | "build:server:prod": "webpack --config ./app/webpack/webpack.server.common.ts --progress --profile --bail", 26 | "lint": "yarn run tslint \"client/**/*.ts\"" 27 | }, 28 | "author": "Jiale Hu ", 29 | "license": "MIT", 30 | "dependencies": { 31 | "@angular/common": "2.4.8", 32 | "@angular/compiler": "2.4.8", 33 | "@angular/core": "2.4.8", 34 | "@angular/flex-layout": "2.0.0-rc.1", 35 | "@angular/forms": "2.4.8", 36 | "@angular/http": "2.4.8", 37 | "@angular/material": "2.0.0-beta.2", 38 | "@angular/platform-browser": "2.4.8", 39 | "@angular/platform-browser-dynamic": "2.4.8", 40 | "@angular/platform-server": "2.4.8", 41 | "@angular/router": "3.4.8", 42 | "@angularclass/form-validators": "^1.0.12", 43 | "@ngrx/core": "^1.2.0", 44 | "@ngrx/store": "^2.2.1", 45 | "ag-grid": "^8.1.1", 46 | "ag-grid-angular": "8.1.0", 47 | "angular-router-loader": "^0.4.0", 48 | "body-parser": "^1.15.2", 49 | "bunyan": "^1.8.3", 50 | "chalk": "^1.1.3", 51 | "compression": "^1.6.2", 52 | "core-js": "^2.4.1", 53 | "cors": "^2.8.0", 54 | "feathers": "~2.0.3", 55 | "feathers-authentication": "~1.1.1", 56 | "feathers-authentication-client": "~0.3.1", 57 | "feathers-authentication-jwt": "~0.3.1", 58 | "feathers-authentication-local": "~0.3.3", 59 | "feathers-configuration": "~0.4.1", 60 | "feathers-errors": "^2.5.0", 61 | "feathers-hooks": "^1.8.1", 62 | "feathers-mongoose": "^5.0.3", 63 | "feathers-permissions": "0.1.1", 64 | "feathers-rest": "^1.7.1", 65 | "feathers-socketio": "^1.5.2", 66 | "mongoose": "^4.8.6", 67 | "rxjs": "~5.2.0", 68 | "zone.js": "~0.7.8" 69 | }, 70 | "devDependencies": { 71 | "@angular/compiler-cli": "2.4.8", 72 | "@angularclass/hmr": "1.2.2", 73 | "@angularclass/hmr-loader": "3.0.2", 74 | "@ngrx/store-devtools": "3.2.3", 75 | "@ngrx/store-log-monitor": "3.0.2", 76 | "@types/body-parser": "^0.0.34", 77 | "@types/bunyan": "^0.0.35", 78 | "@types/chalk": "^0.4.31", 79 | "@types/compression": "^0.0.33", 80 | "@types/core-js": "^0.9.35", 81 | "@types/cors": "0.0.31", 82 | "@types/mongoose": "^4.5.38", 83 | "@types/node": "^7.0.0", 84 | "add-asset-html-webpack-plugin": "^1.0.2", 85 | "angular2-template-loader": "^0.6.0", 86 | "assets-webpack-plugin": "^3.5.1", 87 | "awesome-typescript-loader": "~3.0.0-beta.18", 88 | "codelyzer": "~2.0.0-beta.4", 89 | "copy-webpack-plugin": "^4.0.0", 90 | "css-loader": "^0.26.0", 91 | "exports-loader": "^0.6.3", 92 | "expose-loader": "^0.7.1", 93 | "extract-text-webpack-plugin": "~2.0.0-rc.3", 94 | "file-loader": "^0.10.0", 95 | "html-webpack-plugin": "^2.28.0", 96 | "json-loader": "^0.5.4", 97 | "ng-router-loader": "^2.1.0", 98 | "ngc-webpack": "1.1.0", 99 | "node-sass": "^4.5.0", 100 | "nodemon": "^1.11.0", 101 | "optimize-js-plugin": "0.0.4", 102 | "raw-loader": "^0.5.1", 103 | "rimraf": "^2.5.2", 104 | "sass-loader": "^6.0.0", 105 | "script-ext-html-webpack-plugin": "^1.6.2", 106 | "style-loader": "^0.13.1", 107 | "to-string-loader": "^1.1.4", 108 | "ts-node": "^2.0.0", 109 | "tslib": "^1.5.0", 110 | "tslint": "~4.4.2", 111 | "tslint-loader": "^3.3.0", 112 | "typescript": "~2.1.6", 113 | "url-loader": "^0.5.7", 114 | "webpack": "2.2.0", 115 | "webpack-dev-middleware": "^1.10.0", 116 | "webpack-dev-server": "2.4.1", 117 | "webpack-dll-bundles-plugin": "^1.0.0-beta.5", 118 | "webpack-hot-middleware": "2.17.1", 119 | "webpack-merge": "~3.0.0" 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /process.yml: -------------------------------------------------------------------------------- 1 | apps: 2 | - script: dist/server/server.bundle.js 3 | name: app 4 | watch: false 5 | # instances: 4 6 | # exec_mode: cluster 7 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | import app from './app/app' 2 | const port = app.get('port') 3 | const server = app.listen(port) 4 | 5 | server.on('listening', () => 6 | console.log(`Famn application started on ${app.get('host')}:${port}`) 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /tsconfig.client.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "sourceMap": true, 10 | "noEmit": true, 11 | "noEmitHelpers": true, 12 | "importHelpers": true, 13 | "strictNullChecks": false, 14 | "lib": [ 15 | "es2015", 16 | "dom" 17 | ], 18 | "typeRoots": [ 19 | "node_modules/@types" 20 | ], 21 | "types": [ 22 | "node" 23 | ] 24 | }, 25 | "exclude": [ 26 | "node_modules", 27 | "dist", 28 | "public", 29 | "client/**/*.spec.ts", 30 | "client/**/*.e2e.ts" 31 | ], 32 | "awesomeTypescriptLoaderOptions": { 33 | "forkChecker": true, 34 | "useWebpackText": true 35 | }, 36 | "angularCompilerOptions": { 37 | "genDir": "./aot", 38 | "skipMetadataEmit": true 39 | }, 40 | "compileOnSave": false, 41 | "buildOnSave": false, 42 | "atom": { 43 | "rewriteTsconfig": false 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "removeComments": true, 8 | "noLib": false, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "sourceMap": true, 12 | "pretty": true, 13 | "allowUnreachableCode": false, 14 | "allowUnusedLabels": false, 15 | "noImplicitAny": false, 16 | "noImplicitReturns": true, 17 | "noImplicitUseStrict": false, 18 | "noFallthroughCasesInSwitch": true, 19 | "noEmitHelpers": true 20 | }, 21 | "lib": [ 22 | "dom", 23 | "es6" 24 | ], 25 | "typeRoots": [ 26 | "node_modules/@types" 27 | ], 28 | "types": [ 29 | "core-js", 30 | "jasmine", 31 | "node", 32 | "source-map", 33 | "uglify-js", 34 | "webpack", 35 | "mongoose" 36 | ], 37 | "exclude": [ 38 | "client", 39 | "node_modules", 40 | "public", 41 | "dist" 42 | ], 43 | "awesomeTypescriptLoaderOptions": { 44 | "forkChecker": true, 45 | "useWebpackText": true 46 | }, 47 | "compileOnSave": false, 48 | "buildOnSave": false, 49 | "atom": { "rewriteTsconfig": false } 50 | } 51 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | // "tslint:recommended" 4 | ], 5 | "rulesDirectory": [ 6 | // "node_modules/codelyzer" 7 | ], 8 | "rules": { 9 | // Custom 10 | "trailing-comma": [false, {"multiline": "always", "singleline": "never"}], 11 | "interface-name": [false, "always-prefix"], 12 | // Angular 2 13 | "component-class-suffix": true, 14 | // "component-selector": [true, "element", "my", "kebab-case"], 15 | "directive-class-suffix": true, 16 | // "directive-selector": [true, "attribute", "my", "camelCase"], 17 | "import-destructuring-spacing": true, 18 | "invoke-injectable": true, 19 | "no-access-missing-member": true, 20 | "no-attribute-parameter-decorator": true, 21 | "no-forward-ref": true, 22 | "no-input-rename": true, 23 | "no-output-rename": true, 24 | "pipe-naming": [true, "camelCase", "my"], 25 | "templates-use-public": true, 26 | "use-host-property-decorator": true, 27 | "use-input-property-decorator": true, 28 | "use-life-cycle-interface": true, 29 | "use-output-property-decorator": true, 30 | "use-pipe-transform-interface": true, 31 | // General 32 | "no-console": [true, 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "max-line-length": [ 38 | true, 39 | 100 40 | ], 41 | "no-string-literal": false, 42 | "no-use-before-declare": true, 43 | "object-literal-sort-keys": false, 44 | "ordered-imports": false, 45 | "quotemark": [ 46 | true, 47 | "single", 48 | "avoid-escape" 49 | ], 50 | "semicolon": [true, "never"], 51 | "variable-name": [ 52 | true, 53 | "allow-leading-underscore", 54 | "allow-pascal-case", 55 | "ban-keywords", 56 | "check-format" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Look in `./config` folder for `webpack.*.config.js` 2 | switch (process.env.NODE_ENV) { 3 | case 'prod': 4 | case 'production': 5 | module.exports = require('./client/config/webpack.prod'); 6 | break; 7 | case 'test': 8 | case 'testing': 9 | module.exports = require('./client/config/webpack.test'); 10 | break; 11 | case 'dev': 12 | case 'development': 13 | default: 14 | module.exports = require('./client/config/webpack.dev'); 15 | } 16 | --------------------------------------------------------------------------------