Stefan Huber

init

1 +node_modules/
2 +.vscode/
1 +# Digsig Services
2 +
3 +Reusable and generic digsig Services
4 +
5 +## Repository
6 +
7 + - replicate
8 + - findById
9 + - findByIds
10 + - findByType
11 +
12 +## Rest
13 +
14 + - register
15 + - heartbeat
...\ No newline at end of file ...\ No newline at end of file
1 +export interface DeviceInfo {
2 + id?: string;
3 + hostname?: string;
4 + platform?: string;
5 + arch?: string;
6 + type?: string;
7 + release?: string;
8 + code?: string;
9 +}
1 +export * from './services/rest';
2 +export * from './services/repository';
3 +export * from './services/device';
4 +export * from './api/device-info';
1 +"use strict";
2 +function __export(m) {
3 + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
4 +}
5 +__export(require("./services/rest"));
6 +__export(require("./services/repository"));
7 +__export(require("./services/device"));
1 +export declare class Device {
2 + reload(): void;
3 + exit(): void;
4 +}
1 +"use strict";
2 +var Device = (function () {
3 + function Device() {
4 + }
5 + Device.prototype.reload = function () {
6 + if (window && window.location) {
7 + window.location.reload();
8 + }
9 + };
10 + Device.prototype.exit = function () {
11 + if (window && window['require']) {
12 + var electron = window['require']('electron');
13 + if (electron && electron.ipcRenderer) {
14 + electron.ipcRenderer.send('exit');
15 + }
16 + }
17 + };
18 + return Device;
19 +}());
20 +exports.Device = Device;
1 +import { Rest } from './rest';
2 +import { Device } from './device';
3 +import { DeviceInfo } from '../api/device-info';
4 +export declare class Repository {
5 + protected rest: Rest;
6 + protected device: Device;
7 + protected _db: any;
8 + protected _params: any;
9 + readonly db: any;
10 + constructor(rest: Rest, device: Device);
11 + findById(id: string): Promise<any>;
12 + findByIds(ids: Array<string>): Promise<Array<any>>;
13 + findByType(type: string): Promise<Array<any>>;
14 + replicate(deviceInfo: DeviceInfo): Promise<boolean>;
15 + prepare(url: string): Promise<any>;
16 + init(db_name: string): Promise<any>;
17 + parseUrl(url: string): {
18 + url: string;
19 + user: string;
20 + pass: string;
21 + domain: string;
22 + db_name: string;
23 + };
24 + prepareDocs(res: any): Array<any>;
25 +}
1 +"use strict";
2 +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 + return c > 3 && r && Object.defineProperty(target, key, r), r;
7 +};
8 +var core_1 = require("@angular/core");
9 +var Repository = (function () {
10 + function Repository(rest, device) {
11 + this.rest = rest;
12 + this.device = device;
13 + }
14 + Object.defineProperty(Repository.prototype, "db", {
15 + get: function () {
16 + return this._db;
17 + },
18 + enumerable: true,
19 + configurable: true
20 + });
21 + Repository.prototype.findById = function (id) {
22 + return this._db.get(id);
23 + };
24 + Repository.prototype.findByIds = function (ids) {
25 + var _this = this;
26 + return new Promise(function (resolve, reject) {
27 + _this._db
28 + .allDocs({
29 + include_docs: true,
30 + keys: ids
31 + }).then(function (res) {
32 + resolve(_this.prepareDocs(res));
33 + }).catch(function (error) {
34 + reject(error);
35 + });
36 + });
37 + };
38 + Repository.prototype.findByType = function (type) {
39 + var _this = this;
40 + var options = {
41 + include_docs: true
42 + };
43 + if (type != "") {
44 + options['key'] = type;
45 + }
46 + return new Promise(function (resolve, reject) {
47 + _this._db
48 + .query('index_type/type', options)
49 + .then(function (res) {
50 + resolve(_this.prepareDocs(res));
51 + }).catch(function (error) {
52 + reject(error);
53 + });
54 + });
55 + };
56 + Repository.prototype.replicate = function (deviceInfo) {
57 + var _this = this;
58 + return new Promise(function (resolve, reject) {
59 + _this.rest.heartbeat(deviceInfo)
60 + .then(function (response) {
61 + if (response && response.restart == 1) {
62 + _this.device.reload();
63 + }
64 + else if (response && response.exit == 1) {
65 + _this.device.exit();
66 + }
67 + else {
68 + return _this.prepare(response.db_url);
69 + }
70 + }).then(function () {
71 + var changes = false;
72 + _this._db.replicate.from(_this._params.url)
73 + .once('change', function (info) { changes = true; })
74 + .once('complete', function () { resolve(changes); })
75 + .once('error', function (error) { reject('replication error'); });
76 + })
77 + .catch(function (error) { reject(error); });
78 + });
79 + };
80 + Repository.prototype.prepare = function (url) {
81 + var _this = this;
82 + return new Promise(function (resolve, reject) {
83 + _this._params = _this.parseUrl(url);
84 + if (!_this._params) {
85 + reject();
86 + }
87 + if (_this._db && _this._db.name == _this._params.db_name) {
88 + resolve();
89 + }
90 + else if (!_this._db) {
91 + _this.init(_this._params.db_name).then(function () {
92 + resolve();
93 + });
94 + }
95 + else {
96 + _this._db.destroy()
97 + .then(function () {
98 + return _this.init(_this._params.db_name);
99 + }).then(function () {
100 + resolve();
101 + });
102 + }
103 + });
104 + };
105 + Repository.prototype.init = function (db_name) {
106 + if (window['PouchDB']) {
107 + this._db = new window['PouchDB'](db_name);
108 + }
109 + return this._db.putIfNotExists("_design/index_type", {
110 + views: {
111 + type: {
112 + map: (function (doc) {
113 + if (doc.type) {
114 + emit(doc.type);
115 + }
116 + }).toString()
117 + }
118 + }
119 + });
120 + };
121 + Repository.prototype.parseUrl = function (url) {
122 + // matches: 0:user,1:password,2:domain,3:db_name
123 + var exp = /^https?:\/\/(\w+?):(\w+?)@([a-zA-Z0-9.\-_:]+?)\/([a-zA-Z0-9\-_]+?)$/;
124 + var match = url.match(exp);
125 + if (match) {
126 + return {
127 + url: match[0],
128 + user: match[1],
129 + pass: match[2],
130 + domain: match[3],
131 + db_name: 'local_' + match[4]
132 + };
133 + }
134 + return null;
135 + };
136 + Repository.prototype.prepareDocs = function (res) {
137 + var docs = [];
138 + if (res.total_rows > 0) {
139 + for (var _i = 0, _a = res.rows; _i < _a.length; _i++) {
140 + var row = _a[_i];
141 + docs.push(row.doc);
142 + }
143 + }
144 + return docs;
145 + };
146 + return Repository;
147 +}());
148 +Repository = __decorate([
149 + core_1.Injectable()
150 +], Repository);
151 +exports.Repository = Repository;
1 +import { Http } from '@angular/http';
2 +import { DeviceInfo } from './../api/device-info';
3 +export declare const ERROR_CODE_UNAUTHORIZED: string;
4 +export declare const ERROR_CODE_NOT_FOUND: string;
5 +export declare const ERROR_CODE_SERVER_ERROR: string;
6 +export declare class Rest {
7 + private http;
8 + static serviceUrl: string;
9 + constructor(http: Http);
10 + prepareDeviceInfo(deviceInfo: DeviceInfo, prefixed?: boolean): string;
11 + register(registerCode: string, deviceInfo: DeviceInfo): Promise<any>;
12 + heartbeat(deviceInfo: DeviceInfo): Promise<any>;
13 +}
1 +"use strict";
2 +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 + return c > 3 && r && Object.defineProperty(target, key, r), r;
7 +};
8 +var core_1 = require("@angular/core");
9 +var http_1 = require("@angular/http");
10 +exports.ERROR_CODE_UNAUTHORIZED = "unauthorized";
11 +exports.ERROR_CODE_NOT_FOUND = "not-found";
12 +exports.ERROR_CODE_SERVER_ERROR = "server-error";
13 +var Rest = Rest_1 = (function () {
14 + function Rest(http) {
15 + this.http = http;
16 + }
17 + Rest.prototype.prepareDeviceInfo = function (deviceInfo, prefixed) {
18 + if (prefixed === void 0) { prefixed = true; }
19 + var output = "";
20 + if (deviceInfo.id) {
21 + output += '&device_id=' + deviceInfo.id;
22 + }
23 + if (deviceInfo.hostname) {
24 + output += '&device_hostname=' + deviceInfo.hostname;
25 + }
26 + if (deviceInfo.platform) {
27 + output += '&device_platform=' + deviceInfo.platform;
28 + }
29 + if (deviceInfo.type) {
30 + output += '&device_type=' + deviceInfo.type;
31 + }
32 + if (deviceInfo.release) {
33 + output += '&device_release=' + deviceInfo.release;
34 + }
35 + if (deviceInfo.arch) {
36 + output += '&device_arch=' + deviceInfo.arch;
37 + }
38 + if (deviceInfo.code) {
39 + output += '&register_code=' + deviceInfo.code;
40 + }
41 + // prefixed with & for url
42 + if (!prefixed) {
43 + output = output.substr(1);
44 + }
45 + return output;
46 + };
47 + Rest.prototype.register = function (registerCode, deviceInfo) {
48 + var _this = this;
49 + return new Promise(function (resolve, reject) {
50 + var headers = new http_1.Headers();
51 + headers.append('Content-Type', 'application/x-www-form-urlencoded');
52 + deviceInfo.code = registerCode;
53 + _this.http.post(Rest_1.serviceUrl + "/config/register", _this.prepareDeviceInfo(deviceInfo), { headers: headers }).subscribe(function (response) {
54 + resolve();
55 + }, function (error) {
56 + if (error.status == 401) {
57 + reject(exports.ERROR_CODE_UNAUTHORIZED);
58 + }
59 + else {
60 + reject(exports.ERROR_CODE_SERVER_ERROR);
61 + }
62 + });
63 + });
64 + };
65 + Rest.prototype.heartbeat = function (deviceInfo) {
66 + var _this = this;
67 + return new Promise(function (resolve, reject) {
68 + var headers = new http_1.Headers();
69 + headers.append('Content-Type', 'application/x-www-form-urlencoded');
70 + _this.http.post(Rest_1.serviceUrl + "/config/heartbeat", _this.prepareDeviceInfo(deviceInfo, false), { headers: headers })
71 + .subscribe(function (response) {
72 + resolve(response.json());
73 + }, function (error) {
74 + switch (error.status) {
75 + case 401:
76 + reject(exports.ERROR_CODE_UNAUTHORIZED);
77 + break;
78 + case 404:
79 + reject(exports.ERROR_CODE_NOT_FOUND);
80 + break;
81 + default:
82 + reject(exports.ERROR_CODE_SERVER_ERROR);
83 + break;
84 + }
85 + });
86 + });
87 + };
88 + return Rest;
89 +}());
90 +Rest.serviceUrl = "http://someurl.com";
91 +Rest = Rest_1 = __decorate([
92 + core_1.Injectable()
93 +], Rest);
94 +exports.Rest = Rest;
95 +var Rest_1;
1 +{
2 + "name": "digsig-services",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "dist/index.js",
6 + "module": "dist/index.js",
7 + "typings": "dist/index.d.ts",
8 + "scripts": {
9 + "build": "npm run clean && tsc",
10 + "clean": "rm -rf ./dist",
11 + "pretest": "tsc -p ./spec",
12 + "test": "jasmine .tmp/spec/index.js",
13 + "posttest": "rm -rf .tmp"
14 + },
15 + "author": "",
16 + "license": "ISC",
17 + "devDependencies": {
18 + "@angular/common": "^2.4.8",
19 + "@angular/core": "^2.4.8",
20 + "@angular/http": "^2.4.8",
21 + "@angular/platform-browser": "^2.4.8",
22 + "@types/jasmine": "^2.5.43",
23 + "jasmine": "^2.5.3",
24 + "pouchdb": "^6.1.2",
25 + "pouchdb-upsert": "^2.0.2",
26 + "reflect-metadata": "^0.1.9",
27 + "rxjs": "^5.1.1",
28 + "typescript": "^2.1.6",
29 + "zone.js": "^0.7.7"
30 + },
31 + "peerDependencies": {
32 + "@angular/core": "*",
33 + "@angular/http": "*",
34 + "rxjs": "*",
35 + "pouchdb": "*",
36 + "pouchdb-upsert": "*"
37 + }
38 +}
1 +import 'reflect-metadata';
2 +import './rest';
...\ No newline at end of file ...\ No newline at end of file
1 +import {Rest} from '../src/services/rest';
2 +
3 +describe('Rest API', () => {
4 +
5 + Rest.serviceUrl = "http://digsig.local/config/register";
6 + let restService : Rest = new Rest(null);
7 +
8 + it('Prepare Device Info', () => {
9 +
10 + let deviceString = restService.prepareDeviceInfo({
11 + id : 'some-id' ,
12 + hostname : 'some-hostname' ,
13 + arch : 'some-arch' ,
14 + platform : 'some-platform' ,
15 + type : 'some-type' ,
16 + release : 'some-release' ,
17 + code : 'some-code'
18 + }, false);
19 +
20 + expect(deviceString).toEqual('device_id=some-id&device_hostname=some-hostname&device_platform=some-platform&device_type=some-type&device_release=some-release&device_arch=some-arch&register_code=some-code');
21 + });
22 +
23 +});
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "compilerOptions": {
3 + "target": "es5",
4 + "module": "commonjs",
5 + "moduleResolution": "node",
6 + "sourceMap": false,
7 + "emitDecoratorMetadata": true,
8 + "experimentalDecorators": true,
9 + "lib": [ "es2015", "dom" ],
10 + "noImplicitAny": true,
11 + "suppressImplicitAnyIndexErrors": true,
12 + "outDir": "../.tmp"
13 + }
14 +}
...\ No newline at end of file ...\ No newline at end of file
1 +export interface DeviceInfo {
2 + id?: string;
3 + hostname?: string;
4 + platform?: string;
5 + arch?: string;
6 + type?: string;
7 + release?: string;
8 + code?: string;
9 +}
...\ No newline at end of file ...\ No newline at end of file
1 +export * from './services/rest';
2 +export * from './services/repository';
3 +export * from './services/device';
4 +export * from './api/device-info';
...\ No newline at end of file ...\ No newline at end of file
1 +export class Device {
2 +
3 + reload() {
4 + if (window && window.location) {
5 + window.location.reload();
6 + }
7 + }
8 +
9 + exit() {
10 + if (window && window['require']) {
11 + let electron = window['require']('electron');
12 + if (electron && electron.ipcRenderer) {
13 + electron.ipcRenderer.send('exit');
14 + }
15 + }
16 + }
17 +
18 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import {Injectable} from '@angular/core';
2 +import {Rest} from './rest';
3 +import { Device } from './device';
4 +import {DeviceInfo} from '../api/device-info';
5 +
6 +declare var emit:any;
7 +
8 +@Injectable()
9 +export class Repository {
10 +
11 + protected _db;
12 + protected _params;
13 +
14 + get db() {
15 + return this._db;
16 + }
17 +
18 + constructor(
19 + protected rest:Rest ,
20 + protected device:Device
21 + ) {}
22 +
23 + findById(id:string) : Promise<any> {
24 + return this._db.get(id);
25 + }
26 +
27 + findByIds(ids:Array<string>) : Promise<Array<any>> {
28 + return new Promise<Array<any>>((resolve, reject) => {
29 + this._db
30 + .allDocs({
31 + include_docs: true ,
32 + keys : ids
33 + }).then((res) => {
34 + resolve(this.prepareDocs(res));
35 + }).catch(error => {
36 + reject(error);
37 + });
38 + });
39 + }
40 +
41 + findByType(type:string) : Promise<Array<any>> {
42 + let options = {
43 + include_docs : true
44 + };
45 +
46 + if (type != "") {
47 + options['key'] = type;
48 + }
49 +
50 + return new Promise<Array<any>>((resolve, reject) => {
51 + this._db
52 + .query('index_type/type', options)
53 + .then((res) => {
54 + resolve(this.prepareDocs(res));
55 + }).catch(error => {
56 + reject(error);
57 + });
58 + });
59 + }
60 +
61 + replicate(deviceInfo: DeviceInfo) : Promise<boolean> {
62 + return new Promise<boolean>((resolve, reject) => {
63 + this.rest.heartbeat(deviceInfo)
64 + .then(response => {
65 + if (response && response.restart == 1) {
66 + this.device.reload();
67 + } else if (response && response.exit == 1) {
68 + this.device.exit()
69 + } else {
70 + return this.prepare(response.db_url);
71 + }
72 + }).then(() => {
73 + let changes = false;
74 + this._db.replicate.from(this._params.url)
75 + .once('change', (info) => { changes = true; })
76 + .once('complete', () => { resolve(changes); })
77 + .once('error', (error) => { reject('replication error'); });
78 + })
79 + .catch(error => { reject(error); });
80 + });
81 + }
82 +
83 + prepare(url:string) : Promise<any> {
84 + return new Promise<any>((resolve,reject) => {
85 + this._params = this.parseUrl(url);
86 +
87 + if (!this._params) {
88 + reject();
89 + }
90 +
91 + if (this._db && this._db.name == this._params.db_name) {
92 + resolve();
93 + } else if(!this._db) {
94 + this.init(this._params.db_name).then(() => {
95 + resolve();
96 + });
97 + } else {
98 + this._db.destroy()
99 + .then(() => {
100 + return this.init(this._params.db_name);
101 + }).then(() => {
102 + resolve();
103 + });
104 + }
105 +
106 + });
107 + }
108 +
109 + init(db_name:string) : Promise<any> {
110 + if (window['PouchDB']) {
111 + this._db = new window['PouchDB'](db_name);
112 + }
113 + return this._db.putIfNotExists("_design/index_type", {
114 + views : {
115 + type : {
116 + map : (function(doc) {
117 + if (doc.type) { emit(doc.type); }
118 + }).toString()
119 + }
120 + }
121 + });
122 + }
123 +
124 + parseUrl(url:string) {
125 + // matches: 0:user,1:password,2:domain,3:db_name
126 + let exp = /^https?:\/\/(\w+?):(\w+?)@([a-zA-Z0-9.\-_:]+?)\/([a-zA-Z0-9\-_]+?)$/;
127 + let match = url.match(exp);
128 +
129 + if (match) {
130 + return {
131 + url : match[0] ,
132 + user : match[1] ,
133 + pass : match[2] ,
134 + domain : match[3] ,
135 + db_name : 'local_' + match[4]
136 + }
137 + }
138 +
139 + return null;
140 + }
141 +
142 + prepareDocs(res) : Array<any> {
143 + let docs = [];
144 +
145 + if (res.total_rows > 0) {
146 + for(let row of res.rows) {
147 + docs.push(row.doc);
148 + }
149 + }
150 +
151 + return docs;
152 + }
153 +
154 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import {Injectable} from '@angular/core';
2 +import {Http,Response,Headers} from '@angular/http';
3 +import { DeviceInfo } from './../api/device-info';
4 +
5 +export const ERROR_CODE_UNAUTHORIZED:string = "unauthorized";
6 +export const ERROR_CODE_NOT_FOUND:string = "not-found";
7 +export const ERROR_CODE_SERVER_ERROR:string = "server-error";
8 +
9 +@Injectable()
10 +export class Rest {
11 +
12 + public static serviceUrl: string = "http://someurl.com";
13 +
14 + constructor(private http:Http) {}
15 +
16 + prepareDeviceInfo(deviceInfo:DeviceInfo, prefixed:boolean = true) : string {
17 + let output = "";
18 +
19 + if (deviceInfo.id) {
20 + output += '&device_id='+deviceInfo.id;
21 + }
22 + if (deviceInfo.hostname) {
23 + output += '&device_hostname='+deviceInfo.hostname;
24 + }
25 + if (deviceInfo.platform) {
26 + output += '&device_platform='+deviceInfo.platform;
27 + }
28 + if (deviceInfo.type) {
29 + output += '&device_type='+deviceInfo.type;
30 + }
31 + if (deviceInfo.release) {
32 + output += '&device_release='+deviceInfo.release;
33 + }
34 + if (deviceInfo.arch) {
35 + output += '&device_arch='+deviceInfo.arch;
36 + }
37 + if (deviceInfo.code) {
38 + output += '&register_code='+deviceInfo.code;
39 + }
40 +
41 + // prefixed with & for url
42 + if (!prefixed) {
43 + output = output.substr(1);
44 + }
45 +
46 + return output;
47 + }
48 +
49 + register(registerCode: string, deviceInfo:DeviceInfo) : Promise<any> {
50 + return new Promise<any>((resolve, reject) => {
51 +
52 + var headers = new Headers();
53 + headers.append('Content-Type', 'application/x-www-form-urlencoded');
54 + deviceInfo.code = registerCode;
55 +
56 + this.http.post(Rest.serviceUrl+"/config/register",
57 + this.prepareDeviceInfo(deviceInfo),
58 + { headers: headers }).subscribe((response:Response) => {
59 + resolve();
60 + }, (error:Response) => {
61 + if (error.status == 401) {
62 + reject(ERROR_CODE_UNAUTHORIZED);
63 + } else {
64 + reject(ERROR_CODE_SERVER_ERROR);
65 + }
66 + });
67 + });
68 + }
69 +
70 + heartbeat(deviceInfo:DeviceInfo) : Promise<any> {
71 + return new Promise<any>((resolve, reject) => {
72 +
73 + var headers = new Headers();
74 + headers.append('Content-Type', 'application/x-www-form-urlencoded');
75 +
76 + this.http.post(Rest.serviceUrl+"/config/heartbeat",
77 + this.prepareDeviceInfo(deviceInfo, false),
78 + { headers: headers })
79 + .subscribe((response:Response) => {
80 + resolve(response.json());
81 + },(error:Response) => {
82 + switch (error.status) {
83 + case 401:
84 + reject(ERROR_CODE_UNAUTHORIZED);
85 + break;
86 + case 404:
87 + reject(ERROR_CODE_NOT_FOUND);
88 + break;
89 + default:
90 + reject(ERROR_CODE_SERVER_ERROR);
91 + break;
92 + }
93 + });
94 + });
95 + }
96 +
97 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "compilerOptions": {
3 + "allowSyntheticDefaultImports": true,
4 + "declaration": true,
5 + "experimentalDecorators": true,
6 + "lib": ["dom", "es2015"],
7 + "noImplicitAny": false,
8 + "outDir": "./dist/",
9 + "target": "es5"
10 + },
11 + "exclude": [
12 + "node_modules",
13 + "spec"
14 + ]
15 +}
...\ No newline at end of file ...\ No newline at end of file