User Tools

Site Tools


Sources

expresso/backend/migrations/20181214085626_stable.js

expresso/backend/migrations/20181214085626_stable.js
exports.up = function(knex, Promise) {
    return knex.schema
        .createTable('customers', function(table) {
            table.increments('id').primary().unique()
            table.string('name')
            table.string('phone1')
            table.string('phone2')
            table.string('city')
            table.string('agreement')
            table.timestamp('created_at').defaultTo(knex.fn.now())
            table.timestamp('updated_at').defaultTo(knex.fn.now())
        })
        .createTable('users', function(table) {
            table.increments('id').primary().unique()
            table.string('name')
            table.string('password')
            table.boolean('superuser')
            table.string('gecos')
            table.timestamp('created_at').defaultTo(knex.fn.now())
            table.timestamp('updated_at').defaultTo(knex.fn.now())
        })
}
 
exports.down = function(knex, Promise) {
    return knex.schema
        .dropTable('users')
        .dropTable('customers')
}

expresso/backend/models/customers.js

expresso/backend/models/customers.js
'use strict'
 
module.exports = function(knex) {
 
    var find = function(params) {
        return knex
            .select([
                'customers.*',
            ])
            .from('customers')
            .where(
                'customers.phone1', '~', params.phone1
            )
            .orWhere(
                'customers.phone2', '~', params.phone2
            )
            .orWhere(
                'customers.name', '~', params.name
            )
            .limit(50)
 
    }
 
    var get = function(params) {
        return knex
            .select([
                'customers.*',
            ])
            .from('customers')
            .where({
                'customers.id': params.id,
            })
    }
 
    var create = function(params) {
        return knex
            .insert({
                name: params.name,
                phone1: params.phone1,
                phone2: params.phone2,
                city: params.city,
                agreement: params.agreement,
            })
            .into('customers')
    }
 
 
    return {
        modelName: "customers",
        find: find,
        get: get,
        create: create
    }
}

expresso/backend/models/login.js

expresso/backend/models/login.js
'use strict'
 
module.exports = function(knex) {
 
    var login = function(params) {
        return knex
            .select([
                'users.name',
                'users.gecos',
                'users.id',
                'users.superuser'
            ])
            .from('users')
            .where({
                'users.name': params.name,
                'users.password': params.password,
            })
    }
 
    var check = function(params) {
        return knex
            .select([
                'users.id',
            ])
            .from('users')
            .where({
                'users.id': params.id
            })
    }
 
    return {
        modelName: "login",
        login: login,
        check: check
    }
}

expresso/backend/models/users.js

expresso/backend/models/users.js
'use strict'
 
module.exports = function(knex) {
 
    var list = function(params) {
        return knex
            .select([
                'users.*',
            ])
            .from('users')
            .orderBy('users.name')
    }
 
    var find = function(params) {
        return knex
            .select([
                'users.*',
            ])
            .from('users')
            .where({
                'users.name': params.name,
            })
            .orderBy('users.name')
    }
 
    var get = function(params) {
        return knex
            .select([
                'users.*',
            ])
            .from('users')
            .where({
                'users.id': params.id,
            })
            .orderBy('users.name')
    }
 
    var create = function(params) {
        return knex
            .insert({
                gecos: params.gecos,
                name: params.name,
                password: params.password,
                superuser: params.superuser,
            })
            .into('users')
    }
 
    var update = function(params) {
        return knex
            .update({
                gecos: params.gecos,
                name: params.name,
                password: params.password,
                superuser: params.superuser,
            })
            .from('users')
            .where({
                id: params.id
            })
    }
 
    var drop = function(params) {
        return knex
            .del()
            .from('users')
            .where({
                id: params.id
            })
    }
 
    return {
        modelName: "users",
        list: list,
        find: find,
        create: create,
        update: update,
        drop: drop
    }
}

expresso/backend/plugins/excors.js

expresso/backend/plugins/excors.js
module.exports = function nocors() {
    return function _nocors(req, res, next) {
        res.header('Access-Control-Allow-Origin', '*')
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
        next()
    }
}

expresso/backend/routers/customers.js

expresso/backend/routers/customers.js
'use strict'
 
module.exports = function(knex) {
    const model = require('models/customers')(knex)
    const router = require('routers/exrouter')(model)
    return router
}

expresso/backend/routers/exrouter.js

expresso/backend/routers/exrouter.js
'use strict'
 
 
const lodash = require('lodash')
 
const error = {
    invalidRequest: {
        jsonrpc: "2.0",
        error: {
            code: -32600,
            message: "Invalid Request"
        },
        id: null
    },
    parseError: {
        jsonrpc: "2.0",
        error: {
            code: -32700,
            message: "Parse error"
        },
        id: null
    },
    methodNotFound: {
        jsonrpc: "2.0",
        error: {
            code: -32601,
            message: "Method not found"
        },
        id: 1
    },
    internalError: {
        jsonrpc: "2.0",
        error: {
            code: -32603,
            message: "Internal error"
        },
        id: 0
    }
}
 
module.exports = function(model) {
 
    const express = require('express')
    const router = express.Router()
 
    function responder(req, res) {
 
        console.log({ body: req.body })
 
        if(!lodash.has(req, 'body.method')) {
            res.send(error.invalidRequest)
            return
        }
 
        if(!lodash.has(req, 'body.id')) {
            res.send(error.invalidRequest)
            return
        }
 
        if(!lodash.has(req, 'body.params')) {
            res.send(error.invalidRequest)
            return
        }
 
        if (!lodash.isString(req.body.method)) {
            res.send(error.invalidRequest)
            return
        }
 
        if (!lodash.isString(req.body.id) && !lodash.isNumber(req.body.id)) {
            res.send(error.invalidRequest)
            return
        }
 
        if (typeof(req.body.params) === 'undefined') {
            res.send(error.invalidRequest)
            return
        }
 
        if (typeof(model[req.body.method]) !== 'function') {
            res.send(error.methodNotFound)
            return
        }
 
        const method = req.body.method
        var params = req.body.params
        const id = req.body.id
 
        if (method === 'check') {
            if (lodash.has(req, 'session.userId')) {
                params = { id: req.session.userId }
            } else {
                params = { id: -1 }
            }
        }
 
        var modelPromise = model[method](params)
 
        modelPromise
            .then(function(result) {
 
                if (method === 'login' && lodash.has(result, '[0].id')) {
                        req.session.userId = result[0].id
                        req.session.userProfile = result[0]
                }
 
                if (method === 'check') {
                    console.log({ checkResult: result })
                    if (lodash.has(req, "session.userId") && lodash.has(result, "[0].id")) {
                        result = true
                    } else {
                        result = false
                    }
                }
 
                res.send({
                    jsonrpc: "2.0",
                    result: result,
                    id: id
                })
            })
            .catch(function(err) {
                console.log(err)
                res.send({
                    jsonrpc: "2.0",
                    error: {
                        code: -32603,
                        message: "Internal error"
                    },
                    id: id
                })
            })
    }
 
    router.post('/', responder)
    return router
}

expresso/backend/routers/login.js

expresso/backend/routers/login.js
'use strict'
 
module.exports = function(knex) {
    const model = require('models/login')(knex)
    const router = require('routers/exrouter')(model)
 
    return router
}

expresso/backend/routers/users.js

expresso/backend/routers/users.js
'use strict'
 
module.exports = function(knex) {
    const model = require('models/users')(knex)
    const router = require('routers/exrouter')(model)
    return router
}

expresso/backend/seeds/20181214085527_customers.js

expresso/backend/seeds/20181214085527_customers.js
exports.seed = function(knex, Promise) {
    var rows = []
 
    rows.push({"id":0,"name":"Borodin Oleg","phone1":"79520587264","phone2":"79520587232","city":"Kenigsberg","agreement":"SPN1645","created_at":"2018-12-13T23:36:20.925Z","updated_at":"2018-12-13T23:36:20.925Z"})
 
 
    console.log(rows.length)
    var chunkSize = 50
    return knex('customers').del()
        .then(function() {
            return knex.batchInsert('customers', rows, chunkSize)
        })
}

expresso/backend/seeds/20181214085527_users.js

expresso/backend/seeds/20181214085527_users.js
exports.seed = function(knex, Promise) {
    var rows = []
 
    rows.push({"id":5,"name":"qwerty","password":"12345","superuser":true,"gecos":"Zurb Gnorb","created_at":"2018-11-25T08:36:32.344Z","updated_at":"2018-11-25T08:36:32.344Z"})
    rows.push({"id":2,"name":"kokkolo","password":"12345","superuser":false,"gecos":"Kokkolo Lokkalo","created_at":"2018-12-14T01:36:38.706Z","updated_at":"2018-12-14T01:36:38.706Z"})
    rows.push({"id":1,"name":"user","password":"12345","superuser":false,"gecos":"Anre Brown","created_at":"2018-12-15T08:55:23.180Z","updated_at":"2018-12-15T08:55:23.180Z"})
 
 
    console.log(rows.length)
    var chunkSize = 50
    return knex('users').del()
        .then(function() {
            return knex.batchInsert('users', rows, chunkSize)
        })
}

expresso/backend/utils/exdaemon.js

expresso/backend/utils/exdaemon.js
const child_process = require('child_process')
 
 
module.exports = function(nodeBin) {
 
    function child(exe, args, env) {
        const child = child_process.spawn(exe, args, {
            detached: true,
            stdio: ['ignore', 'ignore', 'ignore'],
            env: env
        })
        child.unref()
        return child
    }
 
    if (process.env.__daemon) {
        return process.pid
    }
    process.env.__daemon = true
 
    var args = [].concat(process.argv)
    var node = args.shift()
    var env = process.env
    child(node, args, env)
    return process.exit()
}

expresso/backend/utils/exlog.js

expresso/backend/utils/exlog.js
const path = require('path')
const fs = require('fs')
const util = require('util')
const exmkdir = require('exmkdir')
 
module.exports = function(logDir) {
 
    exmkdir(logDir)
 
    if (!exmkdir(logDir)) {
        console.log('Cannot write to log directory ' + logDir + '. Exit process.')
        process.exit(1)
    }
 
    var consoleLog = function(data) {
        var date = new Date().toISOString()
        const logFile = fs.createWriteStream(logDir + '/debug.log', {flags : 'a'})
        const logStdout = process.stdout
        logFile.write(date + ' ' + util.format(data) + '\n')
        logStdout.write(date + ' ' + util.format(data) + '\n')
    }
 
    return consoleLog
}

expresso/backend/utils/exmkdir.js

expresso/backend/utils/exmkdir.js
var fs = require('fs')
 
module.exports = function(dir) {
        var dummyFile = dir + '/.dummy'
 
        if (fs.existsSync(dir)) {
            try {
                var fd = fs.openSync(dummyFile, 'w')
                fs.writeSync(fd, process.pid)
                fs.closeSync(fd)
                fs.unlinkSync(dummyFile)
                return true
            } catch (err) {
                return false
            }
        }
 
        if (!fs.existsSync(dir)) {
            try { 
                fs.mkdirSync(dir)
                return true
            } catch {
                return false
            }
        }
    }

expresso/backend/utils/expid.js

expresso/backend/utils/expid.js
'use strict'
 
var fs = require('fs')
var path = require('path')
var exmkdir = require('exmkdir')
 
var create = function(pidFile) {
    var pidDir = path.basename(path.dirname(pidFile))
 
    var _write = function(path) {
        try {
            var fd = fs.openSync(path, 'w')
        } catch (err) {
            return false;
        }
        fs.writeSync(fd, process.pid)
        fs.closeSync(fd)
        return true
    }
 
    if (!exmkdir(pidDir)) {
        console.log('Cannot write to  directory ' + pidDir + '. Exit process.')
        process.exit(1)
    }
 
    if (!_write(pidFile)) {
        console.log('Cannot write pid file ' + pidFile)
        process.exit(1)
    }
}
 
var drop = function(path) {
    try {
        fs.unlinkSync(path);
        return true;
    } catch (err) {
        return false;
    }
}
 
 
module.exports = {
    create: create,
    drop: drop
}

expresso/backend/utils/exsig.js

expresso/backend/utils/exsig.js
const expid = require('expid')
 
module.exports = function(pidFile) {
 
    function _exit() {
        expid.drop(pidFile)
        setTimeout(function() { 
            process.exit(0)
        }, 100)
    }
 
    process.on('SIGINT', _exit)
    process.on('SIGTERM', _exit)
 
    process.on('uncaughtException', function (err) {
        if (err) {
            console.log("caughtException but no error msg" + err.stack)
            setTimeout(function() { 
                process.exit(1)
            }, 500)
        }
    })
}

expresso/backend/exconfig.js

expresso/backend/exconfig.js
module.exports = {
    nodeBin: '/usr/local/bin/node',
    appDir: '/home/user/expresso/backend',
    publicDir: '/home/user/expresso/backend/public',
    uploadDir: '/home/user/expresso/backend/uploads',
    port: 3100,
    address: '127.0.0.1',
    runUser: 'ziggi',
    runGroup: 'wheel',
    logDir: '/home/user/expresso/backend/logs',
    pidFile: '/home/user/expresso/backend/run/pid',
    runDir: '/home/user/expresso/backend/run'
}

expresso/backend/csv2knex.js

expresso/backend/csv2knex.js
#!/usr/bin/env node
 
'use strict'
 
const fs = require('fs')
const readline = require('readline')
 
const knexfile = require('./knexfile')
const knex = require('knex')(
    knexfile.development
)
 
const customers = require('./models/customers')(knex)
 
var rd = readline.createInterface({
    input: fs.createReadStream('customers.csv'),
    console: false
})
 
function phoneNormalize(phone) {
    //if (phone.length === 10) {
    //    phone = '7' + phone
    //}
    phone = phone.replace(/[\-.()]/g, '')
    phone = phone.split(' ')[0]
    return phone
}
 
var rows = []
 
//City,Full Name,Phone2,Phone1,Id
// 0          1      2       3     4
rd.on('line', (line) => {
    var arr = line.split(',')
    var customer = {
        agreement: arr[4].trim(),
        name: arr[1].trim(),
        phone1: arr[2].trim(),//.replace(/[()\-.+]/g, ''),
        phone2: arr[3].trim(),//.replace(/[()\-.+]/g, ''),
        city: arr[0].trim() + '',
        //email: arr[6].trim() + ''
    }
 
    customer.phone1 = phoneNormalize(customer.phone1)
    customer.phone2 = phoneNormalize(customer.phone2)
    //console.log(customer)
 
    rows.push(customer)
})
 
 
 
rd.on('close', () => {
    console.log({
        len: rows.length
    })
 
    var chunkSize = 50
    knex.batchInsert('customers', rows, chunkSize)
        .returning('id')
        .then(function(ids) {
        })
        .catch(function(err) {
            console.log(err)
        })
        .finally(function() {
            knex.destroy();
        })
})

expresso/backend/exserv.js

expresso/backend/exserv.js
'use strict'
 
const exconfig = require('exconfig')
 
const path = require('path')
const fs = require('fs')
const util = require('util')
const lodash = require('lodash')
 
const minimist = require('minimist')
var argv = minimist(process.argv.slice(2))
 
if (argv.help) {
    console.log('Expresso sample web application')
    console.log('Usage: expresso [options]')
    console.log('    --daemon daemonize process')
    process.exit()
}
 
//*** make log ***=
var exlog = require('exlog')(exconfig.logDir)
console.log = exlog
console.error = exlog
 
// *** throw ***
//throw new Error('Boum!')
 
// *** attach express plugins ***
const express = require('express')
const helmet = require('helmet')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const compression = require('compression')
const responseTime = require('response-time')
const morgan = require('morgan')
const lowercasePaths = require('express-lowercase-paths')
const excors = require('excors')
 
const app = express()
 
var formatStr = ':date[iso] :remote-addr :method :url :status :res[content-length] :res[content-type] :response-time ms'
var accessLog = fs.createWriteStream(exconfig.logDir + '/access.log', { flags: 'a' })
app.use(morgan(formatStr, { stream: accessLog }))
app.use(morgan(formatStr))
 
app.use(excors())
app.use(compression())
app.use(lowercasePaths())
app.use(helmet())
app.use(express.static(exconfig.publicDir))
app.use(cookieParser())
app.use(bodyParser.json())
app.use(responseTime())
 
// *** create session *** 
const session = require('express-session')
 
const FileStore = require('session-file-store')(session)
app.use(session({
    store: new FileStore({ path: exconfig.runDir }),
    name: 'session',
    secret: 'efwe987ysdf9fsd69f9ds',
    resave: false,
    rolling: true,
    saveUninitialized: true,
    cookie: { 
            secure: false,
            maxAge: 3600 * 60 * 1000,
            httpOnly: false
    }
}))
 
// *** create knex shared object ***
const knexfile = require('knexfile')
const knex = require('knex')(knexfile.development)
 
// *** set routes ***
 
var login = require('./routers/login')(knex)
app.use('/api/login', login)
 
app.use(function(req, res, next) {
    if (typeof(req.session.userId) != 'undefined') {
        req.session.touch()
        next()
    } else {
         res.sendFile(path.join(exconfig.appDir, '/public/index.html'))
    }
})
 
var users = require('./routers/users')(knex)
app.use('/api/users', users)
 
var customers = require('./routers/customers')(knex)
app.use('/api/customers', customers)
 
app.get('/*', function(req, res) {
     res.sendFile(path.join(exconfig.appDir, '/public/index.html'))
})
 
// *** daemonize process *** 
const exdaemon = require('exdaemon')
if(argv.daemon) {
    exdaemon()
}
 
// *** write pid file *** 
const expid = require('expid')
expid.create(exconfig.pidFile)
 
// *** signal ans exeption handling ***
const exsig = require('exsig')
exsig(exconfig.pidFile)
 
// *** listen socket ***
const cluster = require('cluster')
if (cluster.isMaster) {
    var cpuCount = require('os').cpus().length + 1
    for (var i = 0; i < cpuCount; i += 1) {
        cluster.fork();
    }
} else {
    app.listen(exconfig.port, exconfig.address, null, function() {
        try {
            process.setgid(exconfig.runGroup)
            process.setuid(exconfig.runUser)
        } catch (err) {
        console.log('Cannot change process user and group')
            process.exit(1)
        }
    })
}

expresso/backend/knexfile.js

expresso/backend/knexfile.js
var path = require('path')
 
module.exports = {
    development: {
        //debug: true,
        client: 'pg',
        connection:'postgres://pgsql@localhost/expresso',
        useNullAsDefault: true,
        migrations: {
            directory: __dirname + '/migrations'
        },
        seeds: {
            directory: __dirname + '/seeds'
        },
        //pool: { 
        //    min: 0,
        //    max: 10
        //},
        //acquireConnectionTimeout: 10000
    },
    staging: {
        client: 'pg',
        connection:'postgres://localhost/expresso',
        useNullAsDefault: true,
        migrations: {
            directory: __dirname + '/migrations'
        },
        seeds: {
            directory: __dirname + '/seeds'
        }
    },
    production: {
        client: 'pg',
        connection:'postgres://localhost/expresso',
        useNullAsDefault: true,
        migrations: {
            directory: __dirname + '/migrations'
        },
        seeds: {
            directory: __dirname + '/seeds'
        }
    }
}

expresso/backend/knexport.js

expresso/backend/knexport.js
#!/usr/bin/env node
 
'use strict'
 
 
Object.defineProperty(Date.prototype, 'timestamp', {
    value: function() {
        function pad2(n) {
            return (n < 10 ? '0' : '') + n;
        }
 
        return this.getFullYear() +
               pad2(this.getMonth() + 1) + 
               pad2(this.getDate()) +
               pad2(this.getHours()) +
               pad2(this.getMinutes()) +
               pad2(this.getSeconds());
    }
});
 
const timestamp = new Date().timestamp()
 
const exconfig = require('./exconfig')
const knexfile = require(exconfig.appDir + '/knexfile')
const knex = require('knex')(knexfile.development)
const fs = require('fs')
 
//var tableName = 'domains'
var header = function(tableName) {
  return ` 
exports.seed = function(knex, Promise) {
    var rows = []
`
}
 
var footer = function(tableName) {
    return `
 
    console.log(rows.length)
    var chunkSize = 50
    return knex('${tableName}').del()
        .then(function() {
            return knex.batchInsert('${tableName}', rows, chunkSize)
        })
}
`
}
 
var tables = [ 'users', 'customers' ]
 
tables.forEach(function(item) {
    console.log('export table ' + item)
    knex
        .select()
        .from(item)
        .then(function(result) {
            var out = fs.createWriteStream('./seeds/' + timestamp + '_' + item + '.js', {flags : 'w'})
            out.write(header(item) + '\n')
            result.forEach(function(item) {
                out.write('    rows.push(' + JSON.stringify(item) + ')\n')
            })
            out.write(footer(item) + '\n')
            out.close()
 
        })
        .finally(function() {
            knex.destroy();
        })
})

expresso/frontend/src/app/app-footer/app-footer.component.html

expresso/frontend/src/app/app-footer/app-footer.component.html
<div id="app-footer">
    <div class="grid-container">
        <div class="grid-x grid-margin-x align-center">
            <div class="cell medium-8">
                <hr />
 
                <p class="text-center">
                    <small>Made by <a href="http://wiki.unix7.org">Borodin Oleg</a></small>
                </p>
 
            </div>
        </div>
    </div>
</div>

expresso/frontend/src/app/app-footer/app-footer.component.ts

expresso/frontend/src/app/app-footer/app-footer.component.ts
import { Component, OnInit } from '@angular/core';
 
@Component({
  selector: 'app-footer',
  templateUrl: './app-footer.component.html',
  styleUrls: ['./app-footer.component.scss']
})
export class AppFooterComponent implements OnInit {
 
  constructor() { }
 
  ngOnInit() {
  }
 
}

expresso/frontend/src/app/app-header/app-header.component.html

expresso/frontend/src/app/app-header/app-header.component.html
<div id="app-header" class="margin-bottom-2">
 
    <div class="top-bar" id="top-bar">
 
        <div class="top-bar-left" *ngIf="loginService.isSuperuser()">
 
            <ul class="dropdown menu" data-dropdown-menu>
                <li><a href="#"><i class="my-menu-icon"></i></a></li>
                <li class="menu-text">NgII <i class="fi-sheriff-badge" style="font-size: 1.2em;"></i></li>
                <li>
                    <a href="#">Units</a>
                    <ul class="menu hover">
 
                        <li><a routerLink="/customers">Customers</a></li>
                        <li><a routerLink="/users">Users</a></li>
                        <li><a routerLink="/">Home</a></li>
                    </ul>
                </li>
                <li>
                    <a (click)="loginService.logout()">
                        <i class="fi-power" style="font-size: 1.3em;"></i>
                    </a>
                </li>
            </ul>
 
        </div>
 
        <div class="top-bar-left" *ngIf="loginService.isUser() && !loginService.isSuperuser()">
 
            <ul class="dropdown menu" data-dropdown-menu>
                <li><a href="#"><i class="my-menu-icon"></i></a></li>
                <li class="menu-text">NgII</li>
                <li>
                    <a href="#">Units</a>
                    <ul class="menu hover">
 
                        <li><a routerLink="/customers">Customers</a></li>
                        <li><a routerLink="/">Home</a></li>
                    </ul>
                </li>
                <li>
                    <a (click)="loginService.logout()">
                        <i class="fi-power" style="font-size: 1.3em;"></i>
                    </a>
                </li>
            </ul>
 
        </div>
 
 
        <div class="top-bar-left"  *ngIf="!loginService.isLogin()">
            <ul class="dropdown menu" data-dropdown-menu>
                <li><a href="#"><i class="my-menu-icon"></i></a></li>
                <li class="menu-text">NgII</li>
                <li>
                    <a href="#">Units</a>
                    <ul class="menu hover">
                        <li><a routerLink="/">Home</a></li>
                    </ul>
                </li>
 
                <li><a routerLink="/login">Login</a></li>
            </ul>
        </div>
 
    </div>
 
</div>

expresso/frontend/src/app/app-header/app-header.component.ts

expresso/frontend/src/app/app-header/app-header.component.ts
import { Component, OnInit, OnChanges, SimpleChanges } from '@angular/core';
 
import { LoginService } from '../login.service'
 
declare var $ : any
 
@Component({
    selector: 'app-header',
    templateUrl: './app-header.component.html',
    styleUrls: ['./app-header.component.scss']
})
export class AppHeaderComponent implements OnInit, OnChanges {
 
    constructor(
        public loginService: LoginService
    ) {}
 
    ngOnInit() {
        $('#app-header').foundation()
    }
 
    ngAfterViewInit() {
        $('#app-header').foundation()
    }
 
    ngOnChanges(changes: SimpleChanges) {
        $('#app-header').foundation()
    }
 
    ngAfterContentChecked() {
        $('#app-header').foundation()
    }
 
}

expresso/frontend/src/app/customers/customers.component.html

expresso/frontend/src/app/customers/customers.component.html
<div>
    <app-header></app-header>
 
    <div class="margin-left-2 margin-right-2">
        <div class="grid-container">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-6">
 
                    <div>
                        <form accept-charset="UTF-8"  [formGroup]="phoneForm" (ngSubmit)="searchByPhone(phoneForm)">
 
                            <div class="input-group">
                                <span class="input-group-label">Search</span>
                                <input class="input-group-field" type="text" formControlName="phone">
                                <div class="input-group-button">
                                    <input type="submit" class="button" value="Submit">
                                </div>
                            </div>
 
                        </form>
 
                    </div>
 
                    <div *ngIf="phone && !(customerList.length > 0) ">
                        <p>Yet not found.</p>
                    </div>
 
                    <div *ngIf="customerList.length > 0">
 
                        <h6>{{ timestamp() }}, total <b>{{ customerList.length }}</b></h6>
 
                        <div *ngFor="let item of customerList; let i = index">
                            <hr />
                            <h6>{{ i + 1 }}. <b>{{ item.name }}</b></h6>
 
                            <table>
                                <tbody>
                                    <tr>
                                        <td width="14em">Location</td>
                                        <td><b>{{ item.city }}</b></td>
                                    </tr>
                                    <tr>
                                        <td>Order</td>
                                        <td>{{ item.agreement }}</td>
                                    </tr>
                                    <tr>
                                        <td>Phone 1</td>
                                        <td><a (click)="search(item.phone1)">{{ item.phone1 }}</a></td>
                                    </tr>
                                    <tr>
                                        <td>Phone 2</td>
                                        <td><a (click)="search(item.phone2)">{{ item.phone2 }}</a></td>
                                    </tr>
 
                                </tbody>
                            </table>
 
                        </div>
 
 
                        <p>
                            <a (click)="router.navigate([ '/' ])">
                                <i class="fi-arrow-left"></i> Back to home
                            </a>
                        </p>
 
                    </div>
                </div>
            </div>
 
        </div>
    </div>
 
    <app-footer></app-footer>
</div>

expresso/frontend/src/app/customers/customers.component.ts

expresso/frontend/src/app/customers/customers.component.ts
import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core'
 
import { Router, ActivatedRoute } from '@angular/router'
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'
 
import { AppHeaderComponent } from '../app-header/app-header.component'
import { AppFooterComponent } from '../app-footer/app-footer.component'
 
import { RPCService, RPCResponce, RPCError } from '../rpc.service'
import { CustomersService } from '../customers.service'
import { Customer } from '../models/customer.model'
 
import * as moment from 'moment-mini'
 
@Component({
  selector: 'domain',
  templateUrl: './customers.component.html',
  styleUrls: ['./customers.component.scss']
})
export class CustomersComponent implements OnInit {
 
    phoneForm: FormGroup
 
    phone: string = ''
 
    customer: Customer = {
        id: -1,
        name: '',
        phone1: '',
        phone2: '',
        city: '',
        agreement: ''
    }
 
    customerList: Customer[] = []
 
    constructor(
        private formBuilder: FormBuilder,
        private router: Router,
        private route: ActivatedRoute,
        private customersService: CustomersService
    ) {}
 
    timestamp() {
        return moment().format('hh:mm:ss a')
    }
 
    searchByPhone(form) {
        let phone = form.value.phone
        this.router.navigate([ '/customers/' + phone ])
    }
 
    search(some) {
        this.router.navigate([ '/customers/' + some ])
    }
 
    ngOnChanges(changes: SimpleChanges) {
    }
 
    ngOnInit() {
        this.phoneForm = this.formBuilder.group({
            phone: [ this.phone ],
        })
 
        this.route.paramMap.subscribe(params => {
            this.phone = params.get('phone')
 
            this.phone = this.phone.replace(/[\-.()+]/g, '')
 
            this.phoneForm = this.formBuilder.group({
                phone: [ this.phone ],
            })
 
            if (this.phone) {
                let customer : Customer = {
                    phone1: this.phone,
                    phone2: this.phone,
                    name: this.phone
                }
                this.customersService
                    .find(customer)
                    .subscribe((res: RPCResponce<Customer[]>) => {
                        if (res.result.length > 0) {
                            this.customerList = res.result
                        }
                    })
            }
 
        })
    }
 
 
}

expresso/frontend/src/app/guards/superlogin.guard.ts

expresso/frontend/src/app/guards/superlogin.guard.ts
import { Injectable } from '@angular/core'
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router"
import { Observable } from "rxjs"
 
import * as Cookies from 'es-cookie'
 
import { LoginService } from '../login.service'
 
 
@Injectable()
export class SuperloginGuard implements CanActivate {
 
    cookieName: string = 'session'
 
    constructor(
        private loginService: LoginService,
        private router: Router
    ) {}
 
    canActivate(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ) : Observable<boolean> | boolean {
 
        if (this.loginService.isSuperuser()) {
            return true
        }
        this.loginService.returnUrl = state.url
        this.router.navigate(['/login'])
        return false
    }
}

expresso/frontend/src/app/guards/login.guard.ts

expresso/frontend/src/app/guards/login.guard.ts
import { Injectable } from '@angular/core'
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router"
import { Observable } from "rxjs"
 
import * as Cookies from 'es-cookie'
 
import { LoginService } from '../login.service'
 
 
@Injectable()
export class LoginGuard implements CanActivate {
 
 
    constructor(
        private loginService: LoginService,
        private router: Router
    ) {}
 
    canActivate(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ) : Observable<boolean> | boolean {
 
 
        if (this.loginService.isUser()) {
            return true
        }
        this.loginService.returnUrl = state.url
        this.router.navigate(['/login'])
        return false
    }
}

expresso/frontend/src/app/home/home.component.html

expresso/frontend/src/app/home/home.component.html
<div>
    <app-header></app-header>
 
    <div class="margin-left-2 margin-right-2">
 
 
        <div id="lorem" class="grid-container">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-8">
 
                    <h5>Sample call from:</h5>
 
                    <ul class="list-square">
                        <li *ngFor="let item of samplePhoneList; let i = index">
                            <a target="_blank" rel="noopener noreferrer" href='#/customers/{{ item }}'>{{ item }}</a>
                        </li>
                    </ul>
 
                    <h5>Sample search from parial string:</h5>
 
                    <ul class="list-square">
                        <li *ngFor="let item of partialSamples; let i = index">
                            <a target="_blank" rel="noopener noreferrer" href='#/customers/{{ item }}'>{{ item }}</a>
                        </li>
                    </ul>
 
 
                </div>
            </div>
        </div>
 
    </div>
 
    <app-footer></app-footer>
 
</div>

expresso/frontend/src/app/home/home.component.ts

expresso/frontend/src/app/home/home.component.ts
import { Component, OnInit } from '@angular/core';
 
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
 
    samplePhoneList: string[] = [
        '16192188689',
        '1141855692',
        '7118221699',
        '6315369258',
        '18549937810',
        '16192',
    ]
 
    partialSamples: string[] = [
        '16192',
        '7911',
        'Anre',
        'Princess',
    ]
 
    ngOnInit() {
    }
}

expresso/frontend/src/app/login/login.component.html

expresso/frontend/src/app/login/login.component.html
<div>
 
    <div class="top-bar">
        <div class="top-bar-left padding-left-2">
            <ul class="menu">
                <li class="menu-text"><i class="fi-shield" style="font-size: 1.3em;"></i></li>
                <li class="menu-text">Login</li>
            </ul>
        </div>
    </div>
 
 
    <div class="grid-container">
        <div class="grid-x grid-padding-x align-center">
            <div class="cell small-8 medium-4 large-3">
 
                <div class="card padding-2 margin-top-3">
 
                    <form accept-charset="UTF-8" [formGroup]="loginForm" (ngSubmit)="login(loginForm)">
                        <label>Login name (demo: user)
                            <input type="text" formControlName="name">
                        </label>
                        <label>Password (demo: 12345)
                            <input type="password" formControlName="password">
                        </label>
                        <div class="text-center">
                            <button class="button small" type="submit">Submit</button>
                        </div>
                    </form>
 
                    <div class="margin-2">
                        <div class="text-center">
                            {{ message }}
                        </div>
                    </div>
 
                </div>
 
            </div>
        </div>
    </div>
 
</div>

expresso/frontend/src/app/login/login.component.ts

expresso/frontend/src/app/login/login.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'
import { HttpClient } from '@angular/common/http'
 
import { LoginService } from '../login.service'
 
@Component({
    selector: 'login',
    templateUrl: './login.component.html',
    styleUrls: [ './login.component.scss' ]
})
export class LoginComponent implements OnInit {
 
    loginForm: FormGroup
    message: string = ''
    attemptCount: number = 0
 
    constructor(
        private formBuilder: FormBuilder,
        private loginService: LoginService
    ) {}
 
    login(event) {
        if (this.loginService.login(event.value.name, event.value.password)) {
            this.message = `Wow! Login successful`
            return
        }
        setTimeout(() => {
            this.attemptCount++
            this.message = `Login incorrect. Attempt ${this.attemptCount}`
        }, 1000 )
 
    }
 
    ngOnInit() {
        this.loginForm = this.formBuilder.group({
            name: [ '' ],
            password: [ '' ]
        })
    }
}

expresso/frontend/src/app/models/customer.model.ts

expresso/frontend/src/app/models/customer.model.ts
export interface Customer {
    id?: number
    name?: string
    password?: string
    phone1?: string
    phone2?: string
    agreement?: string
    city?: string
}

expresso/frontend/src/app/models/user.model.ts

expresso/frontend/src/app/models/user.model.ts
export interface User {
    id?: number
    name?: string
    password?: string
    superuser?: boolean
    gecos?: string
}

expresso/frontend/src/app/not-found/not-found.component.html

expresso/frontend/src/app/not-found/not-found.component.html
<div>
 
    <app-header></app-header>
 
    <div class="margin-left-2 margin-right-2">
 
        <div id="lorem" class="grid-container">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-8">
 
                    <h5>Page not found</h5>
 
                </div>
            </div>
        </div>
 
    </div>
 
    <app-footer></app-footer>
 
</div>

expresso/frontend/src/app/not-found/not-found.component.ts

expresso/frontend/src/app/not-found/not-found.component.ts
import { Component, OnInit } from '@angular/core';
 
import { AppHeaderComponent } from '../app-header/app-header.component'
import { AppFooterComponent } from '../app-footer/app-footer.component'
 
 
@Component({
  selector: 'not-found',
  templateUrl: './not-found.component.html',
  styleUrls: ['./not-found.component.scss']
})
export class NotFoundComponent implements OnInit {
 
  ngOnInit() {
  }
 
}

expresso/frontend/src/app/user-create/user-create.component.html

expresso/frontend/src/app/user-create/user-create.component.html
<div *ngIf="show">
 
    <form accept-charset="UTF-8" class="callout" [formGroup]="userForm" (ngSubmit)="create(userForm)"  >
 
        <h5 class="text-center">Do create user?</h5>
 
        <label>Login
            <input type="text" formControlName="name" />
        </label>
 
        <label>Password
            <input type="text" formControlName="password" />
        </label>
 
        <label>Name
            <input type="text" formControlName="gecos" />
        </label>
 
        <fieldset class="cell margin-bottom-1 text-center">
            <input id="checkbox-user-as" type="checkbox" formControlName="superuser">
            <label for="checkbox-user-as">as Superuser</label>
        </fieldset>
 
        <div class="text-center">
            <button class="button small margin-left-1 margin-right-1" type="submit">Accept</button>
            <div class="button small margin-left-1 margin-right-1" (click)="escape()">Escape</div>
        </div>
    </form>
 
</div>

expresso/frontend/src/app/user-create/user-create.component.ts

expresso/frontend/src/app/user-create/user-create.component.ts
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'
import { state, query, useAnimation, transition, style, trigger, animate, animateChild } from '@angular/animations';
 
import { RPCService, RPCResponce, RPCError } from '../rpc.service'
import { UsersService } from '../users.service'
import { User } from '../models/user.model'
 
 
@Component({
    selector: 'user-create',
    templateUrl: './user-create.component.html',
    styleUrls: ['./user-create.component.scss'],
    animations: [
    ]
})
export class UserCreateComponent implements OnInit {
 
    userForm: FormGroup
    user: User
    alertMessage: string = ''
    message: string = ''
 
 
    @Input() show: boolean = false
    @Output() escapeEvent = new EventEmitter<boolean>();
    @Output() successEvent = new EventEmitter<boolean>();
 
    constructor(
        private formBuilder: FormBuilder,
        private usersService: UsersService
    ) {}
 
    create(item) {
        this.user = item.value
                this.usersService
                    .create(this.user)
                    .subscribe((res: RPCResponce<any>) => {
                        if (res.result.rowCount > 0) {
                            this.show = false
                            this.successEvent.emit(true)
                        } else {
                            this.show = false
                            this.successEvent.emit(false)
                        }
                    })
    }
 
    escape() {
        this.show = false
        this.escapeEvent.emit(true)
    }
 
    ngOnInit() {
        this.userForm = this.formBuilder.group({
            name: [ '' ],
            password: [ '' ],
            gecos: [ '' ],
            superuser: [ false ]
        })
    }
}

expresso/frontend/src/app/user-drop/user-drop.component.html

expresso/frontend/src/app/user-drop/user-drop.component.html
<div *ngIf="show">
 
    <form accept-charset="UTF-8" class="callout" [formGroup]="userForm" (ngSubmit)="update(userForm)"  >
 
        <h5 class="text-center">Do drop user {{ user.name }}?</h5>
 
        <div class="text-center">
            <button class="button small margin-left-1 margin-right-1" type="submit">Accept</button>
            <div class="button small margin-left-1 margin-right-1" (click)="escape()">Escape</div>
        </div>
    </form>
 
</div>

expresso/frontend/src/app/user-drop/user-drop.component.ts

expresso/frontend/src/app/user-drop/user-drop.component.ts
import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core'
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'
 
import { RPCService, RPCResponce, RPCError } from '../rpc.service'
import { UsersService } from '../users.service'
import { User } from '../models/user.model'
 
@Component({
  selector: 'user-drop',
  templateUrl: './user-drop.component.html',
  styleUrls: ['./user-drop.component.scss']
})
export class UserDropComponent implements OnInit {
 
    userForm: FormGroup
    alertMessage: string = ''
    message: string = ''
 
    @Input() show: boolean = false
    @Input() user: User = {
        id: -1,
        name: '',
        password: '',
        gecos: ''
    }
 
    @Output() escapeEvent = new EventEmitter<boolean>();
    @Output() successEvent = new EventEmitter<boolean>();
 
    constructor(
        private formBuilder: FormBuilder,
        private usersService: UsersService
 
    ) {}
 
    create(item) {
        this.user = item.value
    }
 
    escape() {
        this.show = false
        this.escapeEvent.emit(true)
    }
 
    update(item) {
        this.user = item.value
                this.usersService
                    .drop(this.user)
                    .subscribe((res: RPCResponce<any>) => {
                        if (res.result.rowCount > 0) {
                            this.show = false
                            this.successEvent.emit(true)
                        } else {
                            this.show = false
                            this.successEvent.emit(false)
                        }
                    })
    }
 
    ngOnChanges(changes: SimpleChanges) {
        if (changes['user']) {
            this.userForm = this.formBuilder.group({
                id: [ this.user.id ],
            })
        }
    }
 
    ngOnInit() {
    }
}

expresso/frontend/src/app/user-update/user-update.component.html

expresso/frontend/src/app/user-update/user-update.component.html
<div *ngIf="show">
 
    <form accept-charset="UTF-8" class="callout" [formGroup]="userForm" (ngSubmit)="update(userForm)"  >
 
        <h5 class="text-center">Do update user?</h5>
 
        <label>Login
            <input type="text" formControlName="name" />
        </label>
 
        <label>Password
            <input type="text" formControlName="password" />
        </label>
 
        <label>Name
            <input type="text" formControlName="gecos" />
        </label>
 
        <fieldset class="cell margin-bottom-1 text-center">
            <input id="checkbox-user-as" type="checkbox" formControlName="superuser">
            <label for="checkbox-user-as">as Superuser</label>
        </fieldset>
 
        <div class="text-center">
            <button class="button small margin-left-1 margin-right-1" type="submit">Accept</button>
            <div class="button small margin-left-1 margin-right-1" (click)="escape()">Escape</div>
        </div>
    </form>
 
</div>

expresso/frontend/src/app/user-update/user-update.component.ts

expresso/frontend/src/app/user-update/user-update.component.ts
import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core'
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'
 
import { RPCService, RPCResponce, RPCError } from '../rpc.service'
import { UsersService } from '../users.service'
import { User } from '../models/user.model'
 
@Component({
  selector: 'user-update',
  templateUrl: './user-update.component.html',
  styleUrls: ['./user-update.component.scss']
})
export class UserUpdateComponent implements OnInit {
 
    userForm: FormGroup
    alertMessage: string = ''
    message: string = ''
 
    @Input() show: boolean = false
    @Input() user: User = {
        id: -1,
        name: '',
        password: '',
        gecos: ''
    }
 
    @Output() escapeEvent = new EventEmitter<boolean>();
    @Output() successEvent = new EventEmitter<boolean>();
 
    constructor(
        private formBuilder: FormBuilder,
        private usersService: UsersService
 
    ) {}
 
    create(item) {
        this.user = item.value
    }
 
    escape() {
        this.show = false
        this.escapeEvent.emit(true)
    }
 
    update(item) {
        this.user = item.value
                this.usersService
                    .update(this.user)
                    .subscribe((res: RPCResponce<any>) => {
                        if (res.result.rowCount > 0) {
                            this.show = false
                            this.successEvent.emit(true)
                        } else {
                            this.show = false
                            this.successEvent.emit(false)
                        }
                    })
    }
 
    ngOnChanges(changes: SimpleChanges) {
        if (changes['user']) {
            this.userForm = this.formBuilder.group({
                id: [ this.user.id ],
                name: [ this.user.name ],
                password: [ this.user.password ],
                gecos: [ this.user.gecos ],
                superuser: [ this.user.superuser ]
            })
        }
    }
 
    ngOnInit() {
    }
}

expresso/frontend/src/app/users/users.component.html

expresso/frontend/src/app/users/users.component.html
<div id="users">
 
    <app-header></app-header>
 
    <div class="margin-left-2 margin-right-2">
 
        <div id="lorem" class="grid-container">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-4">
 
                    <user-create [show]="showCreateForm" (escapeEvent)="escapeForm()"  (successEvent)="successForm($event)"></user-create>
 
                </div>
            </div>
        </div>
 
        <div class="grid-container">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-4">
 
                    <user-update [user]="currentUser" [show]="showUpdateForm" (escapeEvent)="escapeForm()" (successEvent)="successForm($event)"></user-update>
 
                </div>
            </div>
        </div>
 
 
        <div class="grid-container">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-4">
 
                    <user-drop [user]="currentUser" [show]="showDropForm" (escapeEvent)="escapeForm()" (successEvent)="successForm($event)"></user-drop>
 
                </div>
            </div>
        </div>
 
 
        <div class="grid-container" *ngIf="showListRecords">
            <div class="grid-x grid-margin-x align-center">
                <div class="cell medium-8">
 
                    <h5>Users <a (click)="getList()"><i class="fi-refresh" style="font-size: 1.3em;"></i></a>
                        <button class="float-right small button" (click)="showCreate()"><i class="fi-plus" style="font-size: 1.3em;"></i></button>
                    </h5>
 
                    <table>
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>name</th>
                                <th>login</th>
                                <th>right</th>
                                <th><i class="fi-pencil"></i></th>
                                <th><i class="fi-trash"></i></th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr *ngFor="let item of list; let i = index">
                                <td>{{ i + 1 }}</td>
                                <td>{{ item.gecos  }}</td>
                                <td>{{ item.name  }}</td>
                                <td><span *ngIf="item.superuser"><i class="fi-sheriff-badge" style="font-size: 1.2em;"></i></span></td>
                                <td><a (click)="updateItem(item)"><i class="fi-pencil"></i></a></td>
                                <td><a (click)="dropItem(item)"><i class="fi-trash"></i></a></td>
                            </tr>
                        </tbody>
                    </table>
 
                </div>
            </div>
        </div>
 
    </div>
 
    <app-footer></app-footer>
 
</div>

expresso/frontend/src/app/users/users.component.ts

expresso/frontend/src/app/users/users.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router'
 
import { AppHeaderComponent } from '../app-header/app-header.component'
import { AppFooterComponent } from '../app-footer/app-footer.component'
 
import { UserCreateComponent } from '../user-create/user-create.component'
import { UserUpdateComponent } from '../user-update/user-update.component'
import { UserDropComponent } from '../user-drop/user-drop.component'
 
import { RPCService, RPCResponce, RPCError } from '../rpc.service'
import { UsersService } from '../users.service'
import { User } from '../models/user.model'
 
@Component({
    selector: 'users',
    templateUrl: './users.component.html',
    styleUrls: ['./users.component.scss'],
})
export class UsersComponent implements OnInit {
 
    showCreateForm: boolean = false
    showUpdateForm: boolean = false
    showDropForm: boolean = false
    showListRecords: boolean = true
 
    list: User[] = []
 
    currentUser : User = {
        id: -1,
        name: '',
        password: '',
        gecos: ''
    }
 
    constructor(
        private usersService: UsersService,
    ) {}
 
    dropItem(item: User) {
        this.currentUser = item
        this.showDrop()
    }
 
    updateItem(item: User) {
        this.currentUser = item
        this.showUpdate()
    }
 
    getList() {
        this.usersService
            .list()
            .subscribe((res: RPCResponce<User[]>) => {
                this.list = res.result
            })
    }
 
    ngOnInit() {
        this.getList()
    }
 
    escapeForm() {
        this.showList()
    }
 
    successForm($event) {
        this.getList()
        this.showList()
    }
 
    showCreate() {
        this.showCreateForm = true
        this.showUpdateForm = false
        this.showDropForm = false
        this.showListRecords = false
    }
 
    showUpdate() {
        this.showCreateForm = false
        this.showUpdateForm = true
        this.showDropForm = false
        this.showListRecords = false
    }
 
    showDrop() {
        this.showCreateForm = false
        this.showUpdateForm = false
        this.showDropForm = true
        this.showListRecords = false
    }
 
    showList() {
        this.showCreateForm = false
        this.showUpdateForm = false
        this.showDropForm = false
        this.showListRecords = true
    }
}

expresso/frontend/src/app/customers.service.ts

expresso/frontend/src/app/customers.service.ts
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
import { RPCService, RPCResponce, RPCError } from './rpc.service'
 
import { Customer } from './models/customer.model'
 
@Injectable({
  providedIn: 'root'
})
export class CustomersService {
 
    constructor(
        private rpcService: RPCService
    ) {}
 
    find(customer: Customer) {
        return this.rpcService
            .request<Customer, Customer[]>('/api/customers', 'find', customer)
    }
 
}

expresso/frontend/src/app/app.component.html

expresso/frontend/src/app/app.component.html
<div id="app">
    <router-outlet></router-outlet>
</div>

expresso/frontend/src/app/app.component.ts

expresso/frontend/src/app/app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
 
declare var $ : any
 
@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
    ngOnInit() {
        $(document).foundation()
    }
}

expresso/frontend/src/app/app.module.ts

expresso/frontend/src/app/app.module.ts
import { NgModule } from '@angular/core'
import { LocationStrategy, HashLocationStrategy } from '@angular/common'
import { FormsModule } from '@angular/forms'
import { ReactiveFormsModule } from '@angular/forms'
import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { HttpClientModule } from '@angular/common/http'
 
import { RoutingModule } from './routing.module'
 
import { AppComponent } from './app.component'
 
import { AppHeaderComponent } from './app-header/app-header.component'
import { AppFooterComponent } from './app-footer/app-footer.component'
 
import { HomeComponent } from './home/home.component'
import { CustomersComponent } from './customers/customers.component'
 
import { LoginComponent } from './login/login.component'
import { NotFoundComponent } from './not-found/not-found.component'
 
import { RPCService } from './rpc.service'
import { LoginService } from './login.service'
import { UsersService } from './users.service'
import { CustomersService } from './customers.service'
 
import { LoginGuard } from './guards/login.guard'
import { SuperloginGuard } from './guards/superlogin.guard'
 
import { UsersComponent } from './users/users.component'
import { UserCreateComponent } from './user-create/user-create.component'
import { UserUpdateComponent } from './user-update/user-update.component'
import { UserDropComponent } from './user-drop/user-drop.component'
 
@NgModule({
    declarations: [
        AppHeaderComponent,
        AppFooterComponent,
        AppComponent,
        HomeComponent,
        CustomersComponent,
        UsersComponent,
        LoginComponent,
        NotFoundComponent,
        UserCreateComponent,
        UserUpdateComponent,
        UserDropComponent
    ],
    imports: [
        ReactiveFormsModule,
        FormsModule,
        BrowserModule,
        BrowserAnimationsModule,
        RoutingModule,
        HttpClientModule
    ],
    providers: [
        {provide: LocationStrategy, useClass: HashLocationStrategy},
        LoginService,
        RPCService,
        UsersService,
        CustomersService,
        LoginGuard,
        SuperloginGuard
    ],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}

expresso/frontend/src/app/login.service.ts

expresso/frontend/src/app/login.service.ts
import { Injectable, OnInit } from '@angular/core'
import { Router, ActivatedRoute } from '@angular/router'
import * as Cookies from 'es-cookie'
 
import { RPCService } from './rpc.service'
 
import { User } from './models/user.model'
 
export interface UserLogin {
    name: string
    password: string
}
 
//export enum AccessLevel { Superuser = 'superuser', User = 'user' }
 
@Injectable({
  providedIn: 'root'
})
export class LoginService {
 
    cookieName = 'session'
    returnUrl: string = '/'
 
    loginState: boolean = false
    user: User = { 
        id: -1,
        name: '',
        superuser: false,
        gecos: ''
    }
 
    constructor(
        private route: ActivatedRoute,
        private router: Router,
        private rpc: RPCService
    ) {}
 
 
    isLogin() : boolean {
        if (Cookies.get(this.cookieName)) {
            return true
        }
        return false
    }
 
    isUser() : boolean {
        return this.isLogin()
    }
 
    isSuperuser() : boolean {
        if (this.isLogin() && this.user.superuser) {
            return true
        }
        return false
    }
 
    login(name: string, password: string) : boolean {
        let params : User = {
            name: name,
            password: password,
            id: -1
        }
 
        this.rpc.request<User, User[]>('/api/login', 'login', params)
            .subscribe((res) => {
                if (res.result[0].id >= 0) {
 
                    this.user.id = res.result[0].id
                    this.user.superuser = res.result[0].superuser
                    this.user.gecos = res.result[0].gecos
                    this.router.navigate([ this.returnUrl ])
                }
            })
        if (this.user.id >= 0) return true
        return false
    }
 
    logout() {
        Cookies.remove(this.cookieName)
        this.router.navigate(['/'])
    }
 
    ngOnInit() {
    }
 
}

expresso/frontend/src/app/routing.module.ts

expresso/frontend/src/app/routing.module.ts
import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'
 
import { HomeComponent } from './home/home.component'
import { LoginComponent } from './login/login.component'
import { CustomersComponent } from './customers/customers.component'
import { UsersComponent } from './users/users.component'
import { NotFoundComponent } from './not-found/not-found.component'
 
import { LoginGuard } from './guards/login.guard'
import { SuperloginGuard } from './guards/superlogin.guard'
 
const routes: Routes = [
    {
        path: 'login',
        component: LoginComponent
    },
    {
        path: 'customers',
        component: CustomersComponent,
        canActivate: [ LoginGuard ]
    },
    {
        path: 'customers/:phone',
        component: CustomersComponent,
        canActivate: [ LoginGuard ]
    },
    {
        path: 'users',
        component: UsersComponent,
        canActivate: [ SuperloginGuard ]
    },
    {
        path: '',
        //redirectTo: 'customers',
        //pathMatch: 'full'
        component: HomeComponent,
        //canActivate: [ LoginGuard ]
    },
    {
        path: '**', 
        component: NotFoundComponent,
        //canActivate: [ LoginGuard ]
    }
]
 
@NgModule({
    imports: [ RouterModule.forRoot(routes) ],
    exports: [ RouterModule ]
})
export class RoutingModule {}

expresso/frontend/src/app/rpc.service.ts

expresso/frontend/src/app/rpc.service.ts
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs'
 
import { v4 as uuid } from 'uuid'
 
export interface RPCRequest<TParam> {
    jsonrpc: string
    method: string
    params: TParam
    id: string
}
 
export interface RPCError {
    code?: number
    message?: string
}
 
export interface RPCResponce<TResult> {
    jsonrpc: string
    error?: RPCError
    result?: TResult
    id: string
}
 
@Injectable({
  providedIn: 'root'
})
export class RPCService {
 
    constructor(
        private httpClient: HttpClient
    ) {}
 
    request<TParam, TResult>(url: string, method: string, params: TParam) : Observable<RPCResponce<TResult>> {
 
        let rpcRequest : RPCRequest<TParam> = {
            jsonrpc: '2.0',
            method: method,
            params: params,
            id: uuid()
        }
        return this.httpClient.post<RPCResponce<TResult>>(url, rpcRequest)
    }
}

expresso/frontend/src/app/users.service.ts

expresso/frontend/src/app/users.service.ts
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
import { RPCService, RPCResponce, RPCError } from './rpc.service'
 
import { User } from './models/user.model'
 
@Injectable({
  providedIn: 'root'
})
export class UsersService {
 
    constructor(
        private rpcService: RPCService
    ) {}
 
    list() {
        return this.rpcService
            .request<null, User[]>('/api/users', 'list', null)
    }
 
    create(user: User) {
        return this.rpcService
            .request<User, number>('/api/users', 'create', user)
    }
 
    update(user: User) {
        return this.rpcService
            .request<User, number>('/api/users', 'update', user)
    }
 
    drop(user: User) {
        return this.rpcService
            .request<User, number>('/api/users', 'drop', user)
    }
 
}

expresso/frontend/src/environments/environment.prod.ts

expresso/frontend/src/environments/environment.prod.ts
export const environment = {
  production: true
};

expresso/frontend/src/environments/environment.ts

expresso/frontend/src/environments/environment.ts
export const environment = {
  production: false
};

expresso/frontend/src/polyfills.ts

expresso/frontend/src/polyfills.ts
import 'zone.js/dist/zone';

expresso/frontend/src/index.html

expresso/frontend/src/index.html
<!doctype html>
<html class="no-js" lang="en" dir="ltr">
 
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
    <link rel="stylesheet" href="/foundation-icons.css">
    <title>NgII</title>
    <base href="/">
 
    <link rel="shortcut icon" href="/favicon.ico">
</head>
 
<body id="body">
        <app></app>
</body>
 
</html>

expresso/frontend/src/main.ts

expresso/frontend/src/main.ts
import { enableProdMode } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
 
import { AppModule } from './app/app.module'
 
import { environment } from './environments/environment'
 
if (environment.production) {
  enableProdMode()
}
 
platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(err => console.error(err))

expresso/frontend/src/typings.d.ts

expresso/frontend/src/typings.d.ts
declare var module: NodeModule;
 
interface NodeModule {
    id: string;
}