Skip to content
Snippets Groups Projects
Commit 5bcbfd6c authored by Benoit Lavenier's avatar Benoit Lavenier
Browse files

Add ES store capabilities (connecting to a ucoinj-elasticsearch node)

parent 5eca3d24
No related branches found
No related tags found
No related merge requests found
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
<script src="js/services/utils-services.js"></script> <script src="js/services/utils-services.js"></script>
<script src="js/services/wallet-services.js"></script> <script src="js/services/wallet-services.js"></script>
<script src="js/services/bma-services.js"></script> <script src="js/services/bma-services.js"></script>
<script src="js/services/record-services.js"></script>
<!-- entities --> <!-- entities -->
<script src="js/entity/peer.js"></script> <script src="js/entity/peer.js"></script>
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
<script src="js/controllers/peer-controllers.js"></script> <script src="js/controllers/peer-controllers.js"></script>
<script src="js/controllers/currency-controllers.js"></script> <script src="js/controllers/currency-controllers.js"></script>
<script src="js/controllers/wallet-controllers.js"></script> <script src="js/controllers/wallet-controllers.js"></script>
<script src="js/controllers/record-controllers.js"></script>
<!-- App --> <!-- App -->
<script src="js/app.js"></script> <script src="js/app.js"></script>
......
...@@ -3,7 +3,8 @@ angular.module('cesium.controllers', [ ...@@ -3,7 +3,8 @@ angular.module('cesium.controllers', [
'cesium.home.controllers', 'cesium.home.controllers',
'cesium.wallet.controllers', 'cesium.wallet.controllers',
'cesium.currency.controllers', 'cesium.currency.controllers',
'cesium.wot.controllers' 'cesium.wot.controllers',
'cesium.record.controllers'
]) ])
.config(function($httpProvider) { .config(function($httpProvider) {
......
angular.module('cesium.record.controllers', ['cesium.services'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app.lookup_record', {
url: "/record",
views: {
'menuContent': {
templateUrl: "templates/record/lookup.html",
controller: 'RecordLookupCtrl'
}
}
})
.state('app.view_record', {
url: "/record/:id",
views: {
'menuContent': {
templateUrl: "templates/record/view_record.html",
controller: 'RecordCtrl'
}
}
})
.state('app.add_record', {
url: "/record/add",
views: {
'menuContent': {
templateUrl: "templates/record/edit_record.html",
controller: 'RecordEditCtrl'
}
}
})
.state('app.edit_record', {
url: "/record/:id/edit",
views: {
'menuContent': {
templateUrl: "templates/record/edit_record.html",
controller: 'RecordEditCtrl'
}
}
});
})
.controller('RecordLookupCtrl', RecordLookupController)
.controller('RecordCtrl', RecordController)
.controller('RecordEditCtrl', RecordEditController)
;
function RecordLookupController($scope, Record, $state) {
$scope.queryData = {};
$scope.search = { text: '', results: {} };
function createQuery() {
return {
query: {
match: {
title: $scope.search.text/*,
description: $scope.search.text*/
}
},
highlight: {
fields : {
title : {},
description : {}
}
},
from: 0,
size: 20,
_source: ["title", "time", "description", "pictures"]
}
}
$scope.searchChanged = function() {
$scope.search.text = $scope.search.text.toLowerCase();
if ($scope.search.text.length > 1) {
$scope.search.looking = true;
$scope.queryData = createQuery();
return Record.record.search($scope.queryData)
.then(function(res){
$scope.search.looking = false;
if (res.hits.total == 0) {
$scope.search.results = [];
}
else {
$scope.search.results = res.hits.hits.reduce(function(result, hit) {
var record = hit._source;
record.id = hit._id;
record.type = hit._type;
if (hit.highlight.title) {
record.title = hit.highlight.title[0];
}
if (hit.highlight.description) {
record.description = hit.highlight.description[0];
}
return result.concat(record);
}, []);
}
})
.catch(function(err) {
$scope.search.looking = false;
$scope.search.results = [];
});
}
else {
$scope.search.results = [];
}
};
$scope.select = function(id) {
$state.go('app.view_record', {id: id});
};
}
function RecordController($scope, $ionicModal, Wallet, Record, UIUtils, $state, CryptoUtils, $q) {
$scope.formData = {};
$scope.id = null;
$scope.isMember = false;
$scope.category = {};
$scope.pictures = [];
$scope.$on('$ionicView.enter', function(e, $state) {
if ($state.stateParams && $state.stateParams.id) { // Load by id
$scope.load($state.stateParams.id);
}
else {
$state.go('app.lookup_record');
}
});
$scope.load = function(id) {
UIUtils.loading.show();
$q.all([
Record.record.category.all()
.then(function(categories) {
Record.record.get({id: id})
.then(function (hit) {
$scope.formData = hit._source;
$scope.category = categories[hit._source.category];
$scope.id= hit._id;
if (hit._source.pictures) {
$scope.pictures = hit._source.pictures.reduce(function(res, pic) {
return res.concat({src: pic.src});
}, []);
}/*
if (hit._source.pictures) {
hit._source.pictures.forEach(function(pic) {
$scope.pictures.concat({src: pic.src});
});
}*/
UIUtils.loading.hide();
})
})
]).catch(UIUtils.onError('Could not load record'));
};
$scope.edit = function() {
$state.go('app.edit_record', {id: $scope.id});
};
}
function RecordEditController($scope, $ionicModal, Wallet, Record, UIUtils, $state, CryptoUtils, $q, $ionicPopup) {
$scope.walletData = {};
$scope.formData = {};
$scope.id = null;
$scope.isMember = false;
$scope.category = {};
$scope.pictures = [];
ionic.Platform.ready(function() {
if (!navigator.camera) {
delete $scope.camera; return;
}
$scope.camera = navigator.camera;
});
$scope.$on('$ionicView.enter', function(e, $state) {
$scope.loadWallet()
.then(function(walletData) {
$scope.walletData = walletData;
if ($state.stateParams && $state.stateParams.id) { // Load by id
$scope.load($state.stateParams.id);
}
});
});
$scope.load = function(id) {
UIUtils.loading.show();
$q.all([
Record.record.category.all()
.then(function(categories) {
Record.record.get({id: id})
.then(function (hit) {
$scope.formData = hit._source;
$scope.category = categories[hit._source.category];
$scope.id= hit._id;
if (hit._source.pictures) {
$scope.pictures = hit._source.pictures.reduce(function(res, pic) {
return res.concat({src: pic.src});
}, []);
}
UIUtils.loading.hide();
});
})
])
.catch(UIUtils.onError('Could not load record'))
};
$scope.save = function() {
UIUtils.loading.show();
return $q(function(resolve, reject) {
$scope.formData.pictures = $scope.pictures.reduce(function(res, pic) {
return res.concat({src: pic.src});
}, []);
if (!$scope.id) { // Create
$scope.formData.issuer = $scope.walletData.pubkey;
Record.record.add($scope.formData)
.then(function(id) {
UIUtils.loading.hide();
$state.go('app.view_record', {id: id})
resolve();
})
.catch(UIUtils.onError('Could not save record'));
}
else { // Update
Record.record.update($scope.formData, {id: $scope.id})
.then(function() {
UIUtils.loading.hide();
$state.go('app.view_record', {id: $scope.id})
resolve();
})
.catch(UIUtils.onError('Could not update record'));
}
});
};
// category lookup modal
$ionicModal.fromTemplateUrl('templates/record/modal_category.html', {
scope: $scope,
focusFirstInput: true
}).then(function(modal) {
$scope.lookupModal = modal;
$scope.lookupModal.hide();
});
$scope.openCategoryModal = function() {
// load categories
Record.record.category.all()
.then(function(categories){
$scope.categories = categories;
$scope.lookupModal.show();
});
};
$scope.closeCategoryModal = function() {
$scope.lookupModal.hide();
};
$scope.selectCategory = function(cat) {
if (!cat.parent) return;
$scope.category = cat;
$scope.formData.category = cat.id;
$scope.closeCategoryModal();
};
$scope.openPicturePopup = function() {
$ionicPopup.show({
title: 'Choose picture source :',
buttons: [
{
text: 'Gallery',
type: 'button',
onTap: function(e) {
return navigator.camera.PictureSourceType.PHOTOLIBRARY;
}
},
{
text: '<b>Camera</b>',
type: 'button button-positive',
onTap: function(e) {
return navigator.camera.PictureSourceType.CAMERA;
}
}
]
})
.then(function(sourceType){
$scope.getPicture(sourceType);
});
};
$scope.getPicture = function(sourceType) {
var options = {
quality: 50,
destinationType: navigator.camera.DestinationType.DATA_URL,
sourceType: sourceType,
encodingType: navigator.camera.EncodingType.PNG,
targetWidth : 400,
targetHeight : 400
}
$scope.camera.getPicture(
function (imageData) {
$scope.pictures.push({src: "data:image/png;base64," + imageData});
$scope.$apply();
},
UIUtils.onError('Could not get picture'),
options);
};
$scope.fileChanged = function(event) {
UIUtils.loading.show();
return $q(function(resolve, reject) {
var file = event.target.files[0];
var reader = new FileReader();
reader.addEventListener("load", function () {
console.log(reader.result);
$scope.pictures.push({src: reader.result});
$scope.$apply();
}, false);
if (file) {
reader.readAsDataURL(file);
}
UIUtils.loading.hide();
resolve();
});
};
/*
// See doc :
// http://stackoverflow.com/questions/20958078/resize-base64-image-in-javascript-without-using-canvas
$scope.imageToDataUri function(img, width, height) {
// create an off-screen canvas
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
// set its dimension to target size
canvas.width = width;
canvas.height = height;
// draw source image into the off-screen canvas:
ctx.drawImage(img, 0, 0, width, height);
// encode image to data-uri with base64 version of compressed image
return canvas.toDataURL();
}*/
$scope.auth = function() {
$scope.loadWallet()
.then(function(walletData) {
UIUtils.loading.show();
$scope.walletData = walletData;
Record.auth.token(walletData.keypair)
.then(function(token) {
UIUtils.loading.hide();
console.log('authentication token is:' + token);
})
.catch(onError('Could not computed authentication token'));
})
.catch(onError('Could not computed authentication token'));
};
}
\ No newline at end of file
...@@ -2,5 +2,6 @@ angular.module('cesium.services', [ ...@@ -2,5 +2,6 @@ angular.module('cesium.services', [
'cesium.bma.services', 'cesium.bma.services',
'cesium.crypto.services', 'cesium.crypto.services',
'cesium.utils.services', 'cesium.utils.services',
'cesium.wallet.services']) 'cesium.wallet.services',
'cesium.record.services'])
; ;
angular.module('cesium.record.services', ['ngResource', 'cesium.services'])
.factory('Record', function($http, $q, CryptoUtils) {
function Record(server, wsServer) {
var categories = [];
if (wsServer == "undefined" || wsServer == null) {
wsServer = server;
}
function processError(reject, data) {
if (data != null && data.message != "undefined" && data.message != null) {
reject(data.ucode + ": " + data.message);
}
else {
reject('Unknown error from ucoin node');
}
}
function prepare(uri, params, config, callback) {
var pkeys = [], queryParams = {}, newUri = uri;
if (typeof params == 'object') {
pkeys = _.keys(params);
}
pkeys.forEach(function(pkey){
var prevURI = newUri;
newUri = newUri.replace(new RegExp(':' + pkey), params[pkey]);
if (prevURI == newUri) {
queryParams[pkey] = params[pkey];
}
});
config.params = queryParams;
callback(newUri, config);
}
function getResource(uri) {
return function(params) {
return $q(function(resolve, reject) {
var config = {
timeout: 4000
};
prepare(uri, params, config, function(uri, config) {
$http.get(uri, config)
.success(function(data, status, headers, config) {
resolve(data);
})
.error(function(data, status, headers, config) {
processError(reject, data);
});
});
});
}
}
function postResource(uri) {
return function(data, params) {
return $q(function(resolve, reject) {
var config = {
timeout: 4000,
headers : {'Content-Type' : 'application/json'}
};
prepare(uri, params, config, function(uri, config) {
$http.post(uri, data, config)
.success(function(data, status, headers, config) {
resolve(data);
})
.error(function(data, status, headers, config) {
processError(reject, data);
});
});
});
}
}
function ws(uri) {
var sock = new WebSocket(uri);
return {
on: function(type, callback) {
sock.onmessage = function(e) {
callback(JSON.parse(e.data));
};
}
};
}
function getCategories() {
return $q(function(resolve, reject) {
if (categories.length != 0) {
resolve(categories);
return;
}
getResource('http://' + server + '/store/category/_search?pretty&from=0&size=1000')()
.then(function(res) {
if (res.hits.total == 0) {
categories = [];
}
else {
categories = res.hits.hits.reduce(function(result, hit) {
var cat = hit._source;
cat.id = hit._id;
return result.concat(cat);
}, []);
// add as map also
categories.forEach(function(cat) {
categories[cat.id] = cat;
});
}
resolve(categories);
})
.catch(function(err) {
reject(err);
});
});
}
function getToken(keypair) {
return $q(function(resolve, reject) {
var errorFct = function(err) {
reject(err);
}
var getChallenge = getResource('http://' + server + '/auth');
var postAuth = postResource('http://' + server + '/auth');
getChallenge() // get the challenge phrase to sign
.then(function(challenge) {
CryptoUtils.sign(challenge, keypair) // sign the challenge
.then(function(signature) {
postAuth({
pubkey: CryptoUtils.util.encode_base58(keypair.signPk),
challenge: challenge,
signature: signature
}) // get token
.then(function(token) {
resolve(token)
})
.catch(errorFct);
})
.catch(errorFct);
})
.catch(errorFct);
});
}
function emptyHit() {
return {
_id: null,
_index: null,
_type: null,
_version: null,
_source: {}
}
}
return {
auth: {
get: getResource('http://' + server + '/auth'),
post: postResource('http://' + server + '/auth'),
token: getToken
},
hit: {
empty: emptyHit
},
record: {
get: getResource('http://' + server + '/store/record/:id'),
add: postResource('http://' + server + '/store/record'),
update: postResource('http://' + server + '/store/record/:id'),
searchText: getResource('http://' + server + '/store/record/_search?q=:search'),
search: postResource('http://' + server + '/store/record/_search?pretty'),
category: {
all: getCategories
}
}
}
}
var service = Record('localhost:9200');
//var service = ES('metab.ucoin.fr:9288');
service.instance = Record;
return service;
})
;
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
<a ui-sref="app.explore_currency" class="button button-block button-stable icon icon-left ion-search">Explore a currency</a> <a ui-sref="app.explore_currency" class="button button-block button-stable icon icon-left ion-search">Explore a currency</a>
<a ui-sref="app.lookup_record" class="button button-block button-stable icon icon-left ion-search">Explore records</a>
<button ng-click="login()" ng-show="!isLogged()" class="button button-block button-positive icon icon-left ion-log-in">Login</button> <button ng-click="login()" ng-show="!isLogged()" class="button button-block button-positive icon icon-left ion-log-in">Login</button>
<!-- <button ng-click="addAccount()" ng-show="!isLogged()" class="button button-block button-assertive icon icon-left ion-ios-color-wand">Add an account</button> --> <!-- <button ng-click="addAccount()" ng-show="!isLogged()" class="button button-block button-assertive icon icon-left ion-ios-color-wand">Add an account</button> -->
......
<ion-view view-title="Product" left-buttons="leftButtons">
<ion-nav-buttons side="secondary">
<button class="button button-positive" ng-click="save()">
<i class="icon ion-android-send" ng-if="!id"></i>
<i class="icon ion-android-done" ng-if="id"></i>
</button>
</ion-nav-buttons>
<ion-content>
<div class="scroll">
<div class="list">
<ion-gallery ion-gallery-items="pictures"
ng-if="pictures && pictures.length>0"></ion-gallery>
<div class="item item-icon-right" ng-if="camera">
Add pictures
<a class="dark" href="#" ng-click="openPicturePopup()">
<i class="icon ion-camera"></i>
</a>
</div>
<div class="item item-input item-icon-right" ng-if="!camera" >
<span class="input-label has-input">Add pictures</span>
<input type="file" id="file" accept=".png,.jpeg,.jpg" onchange="angular.element(this).scope().fileChanged(event)"/>
<!--a class="dark" href="#" ng-if="!camera" ng-click="addPictureFile()">
<i class="icon ion-plus"></i>
</a-->
</div>
<span class="item item-icon-left" ng-if="id && formData.issuer">
<i class="icon ion-key"></i>
Issuer
<span class="badge">{{formData.issuer | formatPubkey}}</span>
</span>
<span class="item item-button-right" ng-click="openCategoryModal()">
Category
<span class="badge badge-royal">{{category.name}}</span>&nbsp;
<i class="button button-clear ion-chevron-right"></i>
</span>
<div class="item item-input item-floating-label">
<span class="input-label">Title</span>
<input type="text" placeholder="Title" ng-model="formData.title" />
</div>
<div class="item item-input item-floating-label">
<span class="input-label">Description</span>
<textarea placeholder="Description" ng-model="formData.description"></textarea>
</div>
<div class="item item-floating-label">
<span class="input-label">Location</span>
<div class="item-input-inset">
<label class="item-input-wrapper">
<input type="text" placeholder="Location" ng-model="formData.location">
</label>
<button class="button button-small button-positive" ng-click="localize()" ng-if="location.enable">
<i class="icon ion-pinpoint"></i>
</button>
</div>
</div>
<!--<div class="item item-toggle dark">
Public visibility
<label class="toggle toggle-royal">
<input type="checkbox" ng-model="formData.public">
<div class="track">
<div class="handle"></div>
</div>
</label>
</div>-->
</div>
<div class="scroll-bar scroll-bar-v"></div>
</div>
</ion-content>
</ion-view>
\ No newline at end of file
<ion-view view-title="Explore" left-buttons="leftButtons">
<ion-nav-buttons side="secondary">
<button ui-sref="app.add_record" class="button button-positive">
<i class="icon ion-plus"></i>
Add
</button>
</ion-nav-buttons>
<ion-content class="lookupForm padding">
<label class="item item-input">
<i class="icon ion-search placeholder-icon"></i>
<input type="text" placeholder="Search a record (e.g. car, store)" ng-model="search.text" ng-change="searchChanged()">
</label>
<div class="row">
<div class="col">
<a href="#" class="button button-full button-small button-positive" ng-click="">All</a>
</div>
<div class="col">
<a href="#" class="button button-full button-small button-positive" ng-click="">Image</a>
</div>
<div class="col">
<a href="#" class="button button-full button-small button-positive" ng-click="">Search tool</a>
</div>
</div>
<div class="list list-inset">
<label class="item center" ng-if="search.looking">
<ion-spinner icon="android"></ion-spinner>
</label>
<a class="item row item-product" ng-repeat="found in search.results" ng-click="select('{{found.id}}')">
<div class="col col-2">
<img style="height:70px" ng-src="{{found.pictures[0].src}}" nf-if="found.pictures && found.pictures > 0">
<span nf-if="!found.pictures || found.pictures == 0">&nbsp;</span>
</div>
<div class="col col-80 padding">
<h2 ng-bind-html="found.title"></h2>
<h3 class="light" ng-bind-html="found.description"></h3>
<span class="badge item-note">{{found.time | formatDate}}</span>
</div>
</a>
</div>
</ion-content>
</ion-view>
\ No newline at end of file
<ion-modal-view>
<ion-header-bar class="bar-positive">
<h1 class="title">Category</h1>
<button class="button button-positive" ng-click="closeCategoryModal()">Cancel</button>
</ion-header-bar>
<ion-content class="lookupForm">
<div class="list">
<label class="item item-input">
<i class="icon ion-search placeholder-icon"></i>
<input type="text" placeholder="Search" ng-model="search.text" ng-change="searchChanged()">
</label>
<label class="item center" ng-if="search.looking">
<ion-spinner icon="android"></ion-spinner>
</label>
<a class="item" ng-repeat="found in categories" ng-class="{'item-divider': (found.parent==null)}" href="#" ng-click="selectCategory(found)">
<h2 ng-bind-html="found.name"></h2>
</a>
</div>
</ion-content>
</ion-modal-view>
\ No newline at end of file
<ion-view view-title="{{formData.title}}" left-buttons="leftButtons">
<ion-nav-buttons side="secondary">
<button class="button button-positive" ng-click="edit()" ng-if="!isLogged() || formData.issuer == walletData.pubkey">
<i class="icon ion-android-create"></i>
</button>
</ion-nav-buttons>
<ion-content class="item-text-wrap">
<div class="scroll">
<div class="list">
<ion-gallery ion-gallery-items="pictures" ng-if="pictures && pictures.length>0"></ion-gallery>
<div class="item">
<h2 ng-bind-html="formData.title"></h2>
</div>
<div class="item">
<p ng-bind-html="formData.description"></p>
</div>
<div class="item-divider"></div>
<span class="item item-icon-left">
<i class="icon ion-person"></i>
Issuer
<span class="badge" ng-class="{'badge-positive': isMember, 'badge-assertive': !isMember}">{{formData.issuer | formatPubkey}}</span>
</span>
<div class="item">
Category
<span class="badge badge-positive">{{category.name}}</span>
</div>
</div>
<div class="scroll-bar scroll-bar-v"></div>
</div>
</ion-content>
</ion-view>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment