From 1aeabf5db69ecc48ba87ba2cec9c7459fffaab53 Mon Sep 17 00:00:00 2001 From: Ignacio Rodriguez Date: Sat, 27 Aug 2016 15:03:04 +0700 Subject: [PATCH] incorporated DB, starting to layout svcs --- dist/index.js | 18 +- dist/services/device.js | 5 + dist/services/session.js | 5 + dist/services/user.js | 35 ++++ package.json | 3 +- src/index.ts | 14 +- src/services/device.ts | 3 + src/services/session.ts | 3 + src/services/user.ts | 33 ++++ tsconfig.json | 2 +- typings.json | 7 +- typings/globals/nedb/index.d.ts | 224 +++++++++++++++++++++++ typings/globals/nedb/typings.json | 8 + typings/index.d.ts | 2 + typings/modules/es6-promise/index.d.ts | 78 ++++++++ typings/modules/es6-promise/typings.json | 10 + 16 files changed, 442 insertions(+), 8 deletions(-) create mode 100644 dist/services/device.js create mode 100644 dist/services/session.js create mode 100644 dist/services/user.js create mode 100644 src/services/device.ts create mode 100644 src/services/session.ts create mode 100644 src/services/user.ts create mode 100644 typings/globals/nedb/index.d.ts create mode 100644 typings/globals/nedb/typings.json create mode 100644 typings/modules/es6-promise/index.d.ts create mode 100644 typings/modules/es6-promise/typings.json diff --git a/dist/index.js b/dist/index.js index c93352c..88b0893 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,12 +1,22 @@ "use strict"; -var express = require("express"); -var bodyParser = require("body-parser"); -var app = express(); +const express = require("express"); +const bodyParser = require("body-parser"); +const Device = require("./services/device"); +const User = require("./services/user"); +const Session = require("./services/session"); +let app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); var port = process.env.PORT || 8080; -var switch_status = true; +let switch_status = true; +let user_service = new User.Service(); +let session_service = new Session.Service(); +let device_service = new Device.Service(); var router = express.Router(); +router.post("/signup", (req, res) => { + let user = { username: req.body.username, password: req.body.password }; + user_service.signup(user).then(() => res.json(true)).catch(() => res.json(false)); +}); router.post('/login', function (req, res) { res.json({ token: 'youmadeit' }); }); diff --git a/dist/services/device.js b/dist/services/device.js new file mode 100644 index 0000000..f15ddf9 --- /dev/null +++ b/dist/services/device.js @@ -0,0 +1,5 @@ +"use strict"; +class Service { +} +exports.Service = Service; +; diff --git a/dist/services/session.js b/dist/services/session.js new file mode 100644 index 0000000..f15ddf9 --- /dev/null +++ b/dist/services/session.js @@ -0,0 +1,5 @@ +"use strict"; +class Service { +} +exports.Service = Service; +; diff --git a/dist/services/user.js b/dist/services/user.js new file mode 100644 index 0000000..cfa120c --- /dev/null +++ b/dist/services/user.js @@ -0,0 +1,35 @@ +"use strict"; +const Datastore = require("nedb"); +class Model { +} +exports.Model = Model; +; +class Service { + constructor() { + this.db = new Datastore({ filename: "./users.db", autoload: true }); + } + signup(user) { + return new Promise((resolve, reject) => this.db.insert(user, (err, document) => { + if (err) { + return reject(err); + } + return resolve(); + })); + } + ; + login(username, password) { + return new Promise((resolve, reject) => this.db.findOne({ + username: username, password: password + }, (err, document) => { + if (err) { + return reject(err); + } + else { + return resolve(document); + } + })); + } + ; +} +exports.Service = Service; +; diff --git a/package.json b/package.json index f739fb7..3a0f172 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "homepage": "https://github.com/smartbnbkit/backend#readme", "dependencies": { "body-parser": "^1.15.2", - "express": "^4.14.0" + "express": "^4.14.0", + "nedb": "^1.8.0" }, "devDependencies": { "gulp": "^3.9.1", diff --git a/src/index.ts b/src/index.ts index 987b210..e4a8563 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,10 @@ import express = require("express"); import bodyParser = require("body-parser"); +import * as Device from "./services/device"; +import * as User from "./services/user"; +import * as Session from "./services/session"; + let app = express(); app.use(bodyParser.urlencoded({ extended: true })); @@ -12,9 +16,17 @@ var port = process.env.PORT || 8080; let switch_status = true; +let user_service = new User.Service(); +let session_service = new Session.Service(); +let device_service = new Device.Service(); + var router = express.Router(); +router.post("/signup", (req,res) => { + let user: User.Model = { username: req.body.username, password: req.body.password }; + user_service.signup(user).then(() => res.json(true)).catch(() => res.json(false)); +}); router.post('/login', function(req, res) { - res.json({ token: 'youmadeit' }); + res.json({ token: 'youmadeit' }); }); router.get('/switch', function(req, res) { res.json({ status: switch_status }); diff --git a/src/services/device.ts b/src/services/device.ts new file mode 100644 index 0000000..991001d --- /dev/null +++ b/src/services/device.ts @@ -0,0 +1,3 @@ +export class Service { + +}; diff --git a/src/services/session.ts b/src/services/session.ts new file mode 100644 index 0000000..5487723 --- /dev/null +++ b/src/services/session.ts @@ -0,0 +1,3 @@ +export class Service { + +}; diff --git a/src/services/user.ts b/src/services/user.ts new file mode 100644 index 0000000..f7c54ed --- /dev/null +++ b/src/services/user.ts @@ -0,0 +1,33 @@ +import Datastore = require("nedb"); + +export class Model { + username: string; + password: string; +}; + +export class Service { + db: Datastore; + constructor() { + this.db = new Datastore({filename: "./users.db", autoload: true}); + } + signup(user: Model): Promise { + return new Promise((resolve,reject) => this.db.insert(user, (err,document) => { + if (err) { + return reject(err); + } + return resolve(); + })); + }; + login(username: string, password: string): Promise { + return new Promise((resolve,reject) => this.db.findOne({ + username: username, password: password + }, (err,document) => { + if (err) { + return reject(err); + } + else { + return resolve(document as Model); + } + })); + }; +}; diff --git a/tsconfig.json b/tsconfig.json index 8e1a283..8aa066a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "target": "es5", + "target": "es6", "noImplicitAny": false, "sourceMap": false }, diff --git a/typings.json b/typings.json index 2eb8612..d674ba4 100644 --- a/typings.json +++ b/typings.json @@ -1,4 +1,9 @@ { "name": "smartbnbkit-backend", - "dependencies": {} + "dependencies": { + "es6-promise": "registry:npm/es6-promise#3.0.0+20160723033700" + }, + "globalDependencies": { + "nedb": "registry:dt/nedb#0.0.0+20160505185910" + } } diff --git a/typings/globals/nedb/index.d.ts b/typings/globals/nedb/index.d.ts new file mode 100644 index 0000000..a21d141 --- /dev/null +++ b/typings/globals/nedb/index.d.ts @@ -0,0 +1,224 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/48352f5f291965cd71226283ff3ed79a7b119d2b/nedb/nedb.d.ts +declare module "nedb" { + + class NeDBDataStore { + + constructor(); + constructor(path:string); + constructor(options:NeDB.DataStoreOptions); + + persistence:NeDB.Persistence; + + /** + * Load the database from the datafile, and trigger the execution of buffered commands if any + */ + loadDatabase(cb?:(err:Error)=>void):void; + + /** + * Get an array of all the data in the database + */ + getAllData():Array; + + + /** + * Reset all currently defined indexes + */ + resetIndexes(newData:any):void; + + /** + * Ensure an index is kept for this field. Same parameters as lib/indexes + * For now this function is synchronous, we need to test how much time it takes + * We use an async API for consistency with the rest of the code + * @param {String} options.fieldName + * @param {Boolean} options.unique + * @param {Boolean} options.sparse + * @param {Function} cb Optional callback, signature: err + */ + ensureIndex(options:NeDB.EnsureIndexOptions, cb?:(err:Error)=>void):void; + + /** + * Remove an index + * @param {String} fieldName + * @param {Function} cb Optional callback, signature: err + */ + removeIndex(fieldName:string, cb?:(err:Error)=>void):void; + + /** + * Add one or several document(s) to all indexes + */ + addToIndexes(doc:T):void; + addToIndexes(doc:Array):void; + + /** + * Remove one or several document(s) from all indexes + */ + removeFromIndexes(doc:T):void; + removeFromIndexes(doc:Array):void; + + /** + * Update one or several documents in all indexes + * To update multiple documents, oldDoc must be an array of { oldDoc, newDoc } pairs + * If one update violates a constraint, all changes are rolled back + */ + updateIndexes(oldDoc:T, newDoc:T):void; + updateIndexes(updates:Array<{oldDoc:T; newDoc:T;}>):void; + + /** + * Return the list of candidates for a given query + * Crude implementation for now, we return the candidates given by the first usable index if any + * We try the following query types, in this order: basic match, $in match, comparison match + * One way to make it better would be to enable the use of multiple indexes if the first usable index + * returns too much data. I may do it in the future. + * + * TODO: needs to be moved to the Cursor module + */ + getCandidates(query:any):void; + + /** + * Insert a new document + * @param {Function} cb Optional callback, signature: err, insertedDoc + */ + insert(newDoc:T, cb?:(err:Error, document:T)=>void):void; + + /** + * Count all documents matching the query + * @param {any} query MongoDB-style query + */ + count(query:any, callback:(err:Error, n:number)=>void):void; + count(query:any):NeDB.CursorCount; + + /** + * Find all documents matching the query + * If no callback is passed, we return the cursor so that user can limit, skip and finally exec + * @param {any} query MongoDB-style query + * @param {any} projection MongoDB-style projection + */ + find(query:any, projection:T, callback:(err:Error, documents:Array)=>void):void; + find(query:any, projection:T):NeDB.Cursor; + + /** + * Find all documents matching the query + * If no callback is passed, we return the cursor so that user can limit, skip and finally exec + * * @param {any} query MongoDB-style query + */ + find(query:any, callback:(err:Error, documents:Array)=>void):void; + find(query:any):NeDB.Cursor; + + /** + * Find one document matching the query + * @param {any} query MongoDB-style query + * @param {any} projection MongoDB-style projection + */ + findOne(query:any, projection:T, callback:(err:Error, document:T)=>void):void; + + /** + * Find one document matching the query + * @param {any} query MongoDB-style query + */ + findOne(query:any, callback:(err:Error, document:T)=>void):void; + + /** + * Update all docs matching query v1.7.4 and prior signature. + * For now, very naive implementation (recalculating the whole database) + * @param {any} query + * @param {any} updateQuery + * @param {Object} options Optional options + * options.multi If true, can update multiple documents (defaults to false) + * options.upsert If true, document is inserted if the query doesn't match anything + * @param {Function} cb Optional callback, signature: err, + * numReplaced, + * upsert (set to true if the update was in fact an upsert) + * + * @api private Use Datastore.update which has the same signature + */ + update(query:any, updateQuery:any, options?:NeDB.UpdateOptions, cb?:(err:Error, numberOfUpdated:number, upsert:boolean)=>void):void; + /** + * Update all docs matching query v1.8 signature. + * For now, very naive implementation (recalculating the whole database) + * @param {any} query + * @param {any} updateQuery + * @param {Object} options Optional options + * options.multi If true, can update multiple documents (defaults to false) + * options.upsert If true, document is inserted if the query doesn't match anything + * @param {Function} cb Optional callback, signature: err, + * numAffected, + * affectedDocuments (when returnUpdatedDocs is set to true), obj or array + * upsert (set to true if the update was in fact an upsert) + * + * @api private Use Datastore.update which has the same signature + */ + update(query:any, updateQuery:any, options?:NeDB.UpdateOptions, cb?:(err:Error, numberOfUpdated:number, affectedDocuments:any, upsert:boolean)=>void):void; + + /** + * Remove all docs matching the query + * For now very naive implementation (similar to update) + * @param {Object} query + * @param {Object} options Optional options + * options.multi If true, can update multiple documents (defaults to false) + * @param {Function} cb Optional callback, signature: err, numRemoved + * + * @api private Use Datastore.remove which has the same signature + */ + remove(query:any, options:NeDB.RemoveOptions, cb?:(err:Error, n:number)=>void):void; + remove(query:any, cb?:(err:Error, n:number)=>void):void; + } + + namespace NeDBDataStore {} + export = NeDBDataStore; +} + +declare namespace NeDB { + + interface Cursor { + sort(query:any):Cursor; + skip(n:number):Cursor; + limit(n:number):Cursor; + projection(query:any):Cursor; + exec(callback:(err:Error, documents:Array)=>void):void; + } + + interface CursorCount { + exec(callback:(err:Error, count:number)=>void):void; + } + + interface DataStoreOptions { + filename?:string // Optional, datastore will be in-memory only if not provided + inMemoryOnly?:boolean // Optional, default to false + nodeWebkitAppName?:boolean // Optional, specify the name of your NW app if you want options.filename to be relative to the directory where + autoload?:boolean // Optional, defaults to false + onload?:(error:Error)=>any // Optional, if autoload is used this will be called after the load database with the error object as parameter. If you don't pass it the error will be thrown + afterSerialization?:(line:string)=>string; // (optional): hook you can use to transform data after it was serialized and before it is written to disk. Can be used for example to encrypt data before writing database to disk. This function takes a string as parameter (one line of an NeDB data file) and outputs the transformed string, which must absolutely not contain a \n character (or data will be lost) + beforeDeserialization?:(line:string)=>string; // (optional): reverse of afterSerialization. Make sure to include both and not just one or you risk data loss. For the same reason, make sure both functions are inverses of one another. Some failsafe mechanisms are in place to prevent data loss if you misuse the serialization hooks: NeDB checks that never one is declared without the other, and checks that they are reverse of one another by testing on random strings of various lengths. In addition, if too much data is detected as corrupt, NeDB will refuse to start as it could mean you're not using the deserialization hook corresponding to the serialization hook used before (see below) + corruptAlertThreshold?:number; // (optional): between 0 and 1, defaults to 10%. NeDB will refuse to start if more than this percentage of the datafile is corrupt. 0 means you don't tolerate any corruption, 1 means you don't care + } + + /** + * multi (defaults to false) which allows the modification of several documents if set to true + * upsert (defaults to false) if you want to insert a new document corresponding to the update rules if your query doesn't match anything + */ + interface UpdateOptions { + multi?: boolean; + upsert?: boolean; + returnUpdatedDocs?: boolean + } + + /** + * options only one option for now: multi which allows the removal of multiple documents if set to true. Default is false + */ + interface RemoveOptions { + multi?:boolean + } + + interface EnsureIndexOptions { + fieldName:string; + unique?:boolean; + sparse?:boolean; + } + + interface Persistence { + compactDatafile():void; + setAutocompactionInterval(interval:number):void; + stopAutocompaction():void; + } +} diff --git a/typings/globals/nedb/typings.json b/typings/globals/nedb/typings.json new file mode 100644 index 0000000..3099e76 --- /dev/null +++ b/typings/globals/nedb/typings.json @@ -0,0 +1,8 @@ +{ + "resolution": "main", + "tree": { + "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/48352f5f291965cd71226283ff3ed79a7b119d2b/nedb/nedb.d.ts", + "raw": "registry:dt/nedb#0.0.0+20160505185910", + "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/48352f5f291965cd71226283ff3ed79a7b119d2b/nedb/nedb.d.ts" + } +} diff --git a/typings/index.d.ts b/typings/index.d.ts index dcb9898..47243db 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,3 +1,5 @@ +/// /// /// +/// /// diff --git a/typings/modules/es6-promise/index.d.ts b/typings/modules/es6-promise/index.d.ts new file mode 100644 index 0000000..67c5738 --- /dev/null +++ b/typings/modules/es6-promise/index.d.ts @@ -0,0 +1,78 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/types/npm-es6-promise/fb04188767acfec1defd054fc8024fafa5cd4de7/dist/es6-promise.d.ts +declare module 'es6-promise' { +export interface Thenable { + then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; + then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => void): Thenable; +} + +export class Promise implements Thenable { + /** + * If you call resolve in the body of the callback passed to the constructor, + * your promise is fulfilled with result object passed to resolve. + * If you call reject your promise is rejected with the object passed to resolve. + * For consistency and debugging (eg stack traces), obj should be an instanceof Error. + * Any errors thrown in the constructor callback will be implicitly passed to reject(). + */ + constructor (callback: (resolve : (value?: R | Thenable) => void, reject: (error?: any) => void) => void); + + /** + * onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects. + * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called. + * Both callbacks have a single parameter , the fulfillment value or rejection reason. + * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve. + * If an error is thrown in the callback, the returned promise rejects with that error. + * + * @param onFulfilled called when/if "promise" resolves + * @param onRejected called when/if "promise" rejects + */ + then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => U | Thenable): Promise; + then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => void): Promise; + + /** + * Sugar for promise.then(undefined, onRejected) + * + * @param onRejected called when/if "promise" rejects + */ + catch (onRejected?: (error: any) => U | Thenable): Promise; + + /** + * Make a new promise from the thenable. + * A thenable is promise-like in as far as it has a "then" method. + */ + static resolve (): Promise; + static resolve (value: R | Thenable): Promise; + + /** + * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error + */ + static reject (error: any): Promise; + + /** + * Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects. + * the array passed to all can be a mixture of promise-like objects and other objects. + * The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value. + */ + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable, T10 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable, T7 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable, T6 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable , T5 | Thenable]): Promise<[T1, T2, T3, T4, T5]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable ]): Promise<[T1, T2, T3, T4]>; + static all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable]): Promise<[T1, T2, T3]>; + static all(values: [T1 | Thenable, T2 | Thenable]): Promise<[T1, T2]>; + static all(values: [T1 | Thenable]): Promise<[T1]>; + static all(values: Array>): Promise; + + /** + * Make a Promise that fulfills when any item fulfills, and rejects if any item rejects. + */ + static race (promises: (R | Thenable)[]): Promise; +} + +/** + * The polyfill method will patch the global environment (in this case to the Promise name) when called. + */ +export function polyfill (): void; +} diff --git a/typings/modules/es6-promise/typings.json b/typings/modules/es6-promise/typings.json new file mode 100644 index 0000000..45a6551 --- /dev/null +++ b/typings/modules/es6-promise/typings.json @@ -0,0 +1,10 @@ +{ + "resolution": "main", + "tree": { + "src": "https://raw.githubusercontent.com/types/npm-es6-promise/fb04188767acfec1defd054fc8024fafa5cd4de7/typings.json", + "raw": "registry:npm/es6-promise#3.0.0+20160723033700", + "main": "dist/es6-promise.d.ts", + "name": "es6-promise", + "type": "typings" + } +}