diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 2bdf6b4..2a477a0 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,9 +2,33 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -34,122 +58,154 @@ - - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + - - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + + - - + + + + + + - - + + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -158,31 +214,63 @@ - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -281,23 +369,6 @@ @@ -457,79 +545,7 @@ @@ -618,11 +634,11 @@ + - @@ -1128,12 +1144,12 @@ - + - @@ -1149,12 +1165,12 @@ - + - + @@ -1191,11 +1207,11 @@ - - + - - + + + @@ -1236,184 +1252,233 @@ - + + + + + + - - + + + - + - - + + - + - - + + + - + - - + + + - + - - + + + - + - - + + + - - - - + - - + + + - + - - + + + - + - - + + + - + - - + + + - + - - + + + - + - - + + + + + + + + + + + + + - + - - + + + - - - + + + + + + - - - + + + + + + - - - + + + + + + - + - - + + - + - - + + - + - - + + - + - - - + + + + + + + - + - - + + - + - - + + + - + - + + - + - - + + + - + - - + + + - + + + + + + + + + @@ -1421,7 +1486,7 @@ - + @@ -1429,33 +1494,53 @@ - + - - + + - + - - + + + + + + + + + + + + - + - - + + + + + + + + + + + + - + @@ -1464,23 +1549,23 @@ - + - - + + - + - - + + - + @@ -1488,145 +1573,129 @@ - + - - - - - - - - - - - - - - - - - - - - - - - + + + - + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - + + + + + diff --git a/app.js b/app.js index 680326e..17bb52e 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,7 @@ const favicon = require('serve-favicon'); const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); - +const auth = require("./auth.js"); console.log("ENV: " + process.env.NODE_ENV); const app = express(); @@ -22,8 +22,13 @@ app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); +app.use(auth.initialize()); -app.use('/api/v1', apiV1); +app.use('/api/v1', auth.authenticateJwt(), apiV1); +app.post('/login', auth.authenticateUser(), auth.createToken, function(req, res){ + "use strict"; + res.send({ user: req.user, token: req.token}); +}); app.use('/', express.static(path.join(__dirname, 'www'))); // catch 404 and forward to error handler diff --git a/auth.js b/auth.js index fbe5954..f5b447d 100644 --- a/auth.js +++ b/auth.js @@ -1,2 +1,106 @@ + +const config = require("./config"); + const passport = require('passport'); +const ExtractJwt = require('passport-jwt').ExtractJwt; +const JwtStrategy = require('passport-jwt').Strategy; +const LocalStrategy = require('passport-local').Strategy; +const jwt = require('jsonwebtoken'); +const jwtOptions = {}; +const models = require('./models/index'); +const crypto = require('crypto'); +const userDatabase = config.databases.forum; + +jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeader(); +jwtOptions.secretOrKey = config.jwtKey; +jwtOptions.issuer = 'naranawm.org'; + +passport.use(new JwtStrategy(jwtOptions, function(jwt_payload, done){ + "use strict"; + console.log(jwt_payload); + // Just returning a basic user for now TODO: build this out + return done(null, { id: 1 }); +})); + +// Todo: Move this into an extensions section so others can override +passport.use("learnNaviForum", new LocalStrategy(function(username, password, done){ + "use strict"; + if(username === undefined || password === undefined || username === "" || password === ""){ + return done(null, false, { message: "Incorrect Username / Password" }); + } + + models.sequelize.query(`SELECT ${userDatabase.database}.${userDatabase.table}.id_member, member_name, passwd, real_name, filename FROM ${userDatabase.database}.${userDatabase.table} LEFT JOIN ${userDatabase.database}.${userDatabase.attachmentTable} ON ${userDatabase.database}.${userDatabase.table}.id_member = ${userDatabase.database}.${userDatabase.attachmentTable}.id_member WHERE member_name = :username`, { + type: models.sequelize.QueryTypes.SELECT, + replacements: { + username: username + } + }).then(results => { + "use strict"; + if(results !== null && results.length === 1){ + const hash = crypto.createHash("sha1"); + hash.update(results[0].member_name + password); + const passwordHash = hash.digest("hex"); + if(passwordHash === results[0].passwd){ + // Successful Login!!! + return done(null, { + id: results[0].id_member, + name: results[0].real_name, + username: results[0].member_name, + avatar: "https://forum.learnnavi.org/avs/" + results[0].filename, + provider: "https://forum.learnnavi.org" + }); + } + } + return done(null, false, { message: "Incorrect Username / Password" }); + }); +})); + +// Used for local testing. TODO: Remove this once other authentication strategies are fully functional +passport.use("localTest", new LocalStrategy(function(username, password, done){ + "use strict"; + if(username === undefined || password === undefined || username === "" || password === ""){ + return done(null, false, { message: "Incorrect Username / Password" }); + } + + if(username === "test" && password === "test"){ + return done(null, { + id: 0, + name: "Test User", + username: username, + provider: "https://naranawm.org" + }); + } + + return done(null, false, { message: "Incorrect Username / Password" }); + +})); + +module.exports = { + initialize: function(){ + return passport.initialize(); + }, + authenticateJwt: function () { + return passport.authenticate("jwt", { session: false}); + }, + authenticateUser: function() { + "use strict"; + return passport.authenticate(["localTest", "learnNaviForum"], { session: false}); + }, + createToken: function(req, res, next){ + "use strict"; + const payload = { + id: req.user.id, + name: req.user.name, + username: req.user.username, + avatar: req.user.avatar, + provider: req.user.provider + }; + console.log(payload); + req.token = jwt.sign(payload, config.jwtKey, { + expiresIn: "1d", + issuer: "naranawm.org" + }); + next(); + } +}; diff --git a/bin/rebuild b/bin/rebuild index f0eb3bd..6c7959e 100755 --- a/bin/rebuild +++ b/bin/rebuild @@ -4,7 +4,7 @@ const config = require("../config"); const debug = require('debug')('Naranawm:rebuild'); const Dictionary = require('../eanaEltuMigration/dictionary'); -const dictionary = new Dictionary(config); +const dictionary = new Dictionary(debug); debug("Starting Database Rebuild"); dictionary.buildDictionary(function(){ diff --git a/config/kenten.vault.js b/config/kenten.vault.js index 14aaa85..4e4bd85 100644 Binary files a/config/kenten.vault.js and b/config/kenten.vault.js differ diff --git a/config/local.js b/config/local.js index defc188..ac5e5e3 100644 --- a/config/local.js +++ b/config/local.js @@ -19,7 +19,8 @@ module.exports = { }, forum: { database: "learnnavi_forum", - table: "smf_members" + table: "smf_members", + attachmentTable: "smf_attachments" }, sqlite: { host: "127.0.0.1", @@ -27,6 +28,8 @@ module.exports = { benchmark: false, logging: false } - } + }, + + jwtKey: "arocxg9e8xgh;rchukuthhaetubmapx9i9xcgeu09k" }; diff --git a/eanaEltuMigration/dictionary.js b/eanaEltuMigration/dictionary.js index 3d6fcc1..ee7d9d0 100644 --- a/eanaEltuMigration/dictionary.js +++ b/eanaEltuMigration/dictionary.js @@ -4,14 +4,14 @@ const Lemma = require('./lemma'); const EanaEltu = require('./eanaEltu'); const models = require('../models'); const Promise = require('bluebird'); -const debug = require('debug')('Naranawm:server'); +const debug = require('debug')('Naranawm:rebuild'); /* * This Module / Section is to export data from Eana Eltu * and convert it into a format that we can insert into * the new database schema */ -function Dictionary (config) { +function Dictionary () { this.eanaEltu = new EanaEltu(); this.languages = { en: { diff --git a/package-lock.json b/package-lock.json index c37f2fb..04f774e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -217,6 +217,11 @@ "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base64url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" + }, "basic-auth": { "version": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=" @@ -293,12 +298,6 @@ "hoek": "2.16.3" } }, - "bower": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.0.tgz", - "integrity": "sha1-Vdvr7wrZFVOC2enT5JfBNyNFtEo=", - "dev": true - }, "brace-expansion": { "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", @@ -316,6 +315,11 @@ "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "builtin-modules": { "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" @@ -765,6 +769,15 @@ "jsbn": "0.1.1" } }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "requires": { + "base64url": "2.0.0", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, "editorconfig": { "version": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.13.2.tgz", "integrity": "sha1-jleSbZ7mmrbLmZ8CfCFxRnrM6zU=", @@ -2769,6 +2782,11 @@ "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, + "isemail": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" + }, "isexe": { "version": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" @@ -2791,6 +2809,17 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "joi": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", + "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", + "requires": { + "hoek": "2.16.3", + "isemail": "1.2.0", + "moment": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "topo": "1.1.0" + } + }, "js-beautify": { "version": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.6.14.tgz", "integrity": "sha1-07j3Mi0CuSd9WL0jgmTDJ+WARM0=", @@ -2841,6 +2870,18 @@ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, + "jsonwebtoken": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.2.tgz", + "integrity": "sha1-VxuQPAfodcD8WSA9GseGZ9gOCc0=", + "requires": { + "joi": "6.10.1", + "jws": "3.1.4", + "lodash.once": "4.1.1", + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, "jsprim": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", @@ -2867,6 +2908,27 @@ "promise": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" } }, + "jwa": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "requires": { + "base64url": "2.0.0", + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.9", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "jws": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "requires": { + "base64url": "2.0.0", + "jwa": "1.1.5", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, "kind-of": { "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", @@ -3047,6 +3109,11 @@ "version": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=" }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lodash.restparam": { "version": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" @@ -3627,6 +3694,23 @@ "pause": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" } }, + "passport-jwt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-2.2.1.tgz", + "integrity": "sha1-DgBMlAcTGdZz2dm8/RV0qGgBFSc=", + "requires": { + "jsonwebtoken": "7.4.2", + "passport-strategy": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz" + } + }, "passport-localapikey": { "version": "https://registry.npmjs.org/passport-localapikey/-/passport-localapikey-0.0.3.tgz", "integrity": "sha1-pERzXILME1J2gp3eJ9etAfqj/aA=", @@ -4740,6 +4824,14 @@ "version": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "topo": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", + "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", + "requires": { + "hoek": "2.16.3" + } + }, "toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", diff --git a/package.json b/package.json index c2f9061..d373bf3 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,13 @@ "cookie-parser": "~1.4.3", "debug": "~2.6.3", "express": "~4.15.2", + "jsonwebtoken": "^7.4.2", "morgan": "^1.8.2", "mysql": "^2.14.0", "mysql2": "^1.3.6", "passport": "^0.3.2", + "passport-jwt": "^2.2.1", + "passport-local": "^1.0.0", "passport-localapikey": "0.0.3", "pug": "~2.0.0-beta11", "sequelize": "^4.4.2", diff --git a/routes/apiV1.js b/routes/apiV1.js index 3874319..462bc32 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -5,6 +5,7 @@ const router = express.Router(); router.use('/auth', require('./v1/auth')); router.use('/definitions', require('./v1/definitions')); router.use('/models', require('./v1/models')); +router.use('/rebuild', require('./v1/rebuild')); router.use('/export', require('./v1/export')); module.exports = router; diff --git a/routes/v1/models.js b/routes/v1/models.js index 4bc5a3f..2dd6858 100644 --- a/routes/v1/models.js +++ b/routes/v1/models.js @@ -20,8 +20,21 @@ router.get('/:model', function(req, res, next) { res.status(400).send(`Model [${req.params.model}] does not exist`); return; } - console.log(req.query); - model.findAll({ where: req.query }).then(function (data) { + + const queryOptions = {}; + + if(req.query.limit !== undefined){ + queryOptions.limit = parseInt(req.query.limit); + delete req.query.limit; + } + + if(req.query.offset !== undefined){ + queryOptions.offset = parseInt(req.query.offset); + delete req.query.offset; + } + + queryOptions.where = req.query; + model.findAll(queryOptions).then(function (data) { "use strict"; res.send(data); }).catch(function (error) { diff --git a/routes/v1/rebuild.js b/routes/v1/rebuild.js new file mode 100644 index 0000000..ab37d63 --- /dev/null +++ b/routes/v1/rebuild.js @@ -0,0 +1,24 @@ +const express = require('express'); +const router = express.Router(); +const config = require("../../config"); +const debug = require('debug')('Naranawm:rebuild'); + +const Dictionary = require('../../eanaEltuMigration/dictionary'); + + +/* GET rebuild data from EE */ +router.get('/eanaEltu', function(req, res, next) { + //debug.log = res.write.bind(res); + const dictionary = new Dictionary(); + debug("Starting Database Rebuild..."); + dictionary.buildDictionary(function(){ + // We have a full Dictionary now to do things with :) + + dictionary.export(function(){ + debug("Export Complete!!!"); + res.send("Rebuild Complete!"); + }); + }); +}); + +module.exports = router; diff --git a/www/app.js b/www/app.js index 203f668..bee2cc1 100755 --- a/www/app.js +++ b/www/app.js @@ -2,16 +2,26 @@ // Declare app level module which depends on views, and components angular.module('naranawm', [ - 'ngRoute', - 'ngResource', - 'naranawm.languages', - 'naranawm.sources', - 'naranawm.view1', - 'naranawm.view2', - 'naranawm.version', - 'ui.bootstrap' -]). -config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) { - $locationProvider.hashPrefix('!'); - $routeProvider.otherwise({redirectTo: '/languages'}); + 'ngRoute', + 'ngResource', + 'ngStorage', + 'naranawm.vezeyko', + 'naranawm.version', + 'ui.bootstrap' +]).config(['$locationProvider', '$routeProvider', '$httpProvider', function ($locationProvider, $routeProvider, $httpProvider) { + + + $routeProvider.when('/kelku', { + templateUrl: 'kelku/kelku.html', + controller: 'KelkuCtrl' + }); + + $routeProvider.otherwise({redirectTo: '/kelku'}); + + $httpProvider.interceptors.push('Interceptors'); + +}]).run(['$rootScope', '$location', function($rootScope, $location){ + + + }]); diff --git a/www/assets/library/ngStorage-0.3.11.js b/www/assets/library/ngStorage-0.3.11.js new file mode 100644 index 0000000..3b911b7 --- /dev/null +++ b/www/assets/library/ngStorage-0.3.11.js @@ -0,0 +1,237 @@ +(function (root, factory) { + 'use strict'; + + if (typeof define === 'function' && define.amd) { + define(['angular'], factory); + } else if (root.hasOwnProperty('angular')) { + // Browser globals (root is window), we don't register it. + factory(root.angular); + } else if (typeof exports === 'object') { + module.exports = factory(require('angular')); + } +}(this , function (angular) { + 'use strict'; + + // In cases where Angular does not get passed or angular is a truthy value + // but misses .module we can fall back to using window. + angular = (angular && angular.module ) ? angular : window.angular; + + + function isStorageSupported($window, storageType) { + + // Some installations of IE, for an unknown reason, throw "SCRIPT5: Error: Access is denied" + // when accessing window.localStorage. This happens before you try to do anything with it. Catch + // that error and allow execution to continue. + + // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari + // when "Block cookies": "Always block" is turned on + var supported; + try { + supported = $window[storageType]; + } + catch(err) { + supported = false; + } + + // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage and sessionStorage + // is available, but trying to call .setItem throws an exception below: + // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota." + if(supported) { + var key = '__' + Math.round(Math.random() * 1e7); + try { + $window[storageType].setItem(key, key); + $window[storageType].removeItem(key, key); + } + catch(err) { + supported = false; + } + } + + return supported; + } + + /** + * @ngdoc overview + * @name ngStorage + */ + + return angular.module('ngStorage', []) + + /** + * @ngdoc object + * @name ngStorage.$localStorage + * @requires $rootScope + * @requires $window + */ + + .provider('$localStorage', _storageProvider('localStorage')) + + /** + * @ngdoc object + * @name ngStorage.$sessionStorage + * @requires $rootScope + * @requires $window + */ + + .provider('$sessionStorage', _storageProvider('sessionStorage')); + + function _storageProvider(storageType) { + var providerWebStorage = isStorageSupported(window, storageType); + + return function () { + var storageKeyPrefix = 'ngStorage-'; + + this.setKeyPrefix = function (prefix) { + if (typeof prefix !== 'string') { + throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setKeyPrefix() expects a String.'); + } + storageKeyPrefix = prefix; + }; + + var serializer = angular.toJson; + var deserializer = angular.fromJson; + + this.setSerializer = function (s) { + if (typeof s !== 'function') { + throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setSerializer expects a function.'); + } + + serializer = s; + }; + + this.setDeserializer = function (d) { + if (typeof d !== 'function') { + throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setDeserializer expects a function.'); + } + + deserializer = d; + }; + + this.supported = function() { + return !!providerWebStorage; + }; + + // Note: This is not very elegant at all. + this.get = function (key) { + return providerWebStorage && deserializer(providerWebStorage.getItem(storageKeyPrefix + key)); + }; + + // Note: This is not very elegant at all. + this.set = function (key, value) { + return providerWebStorage && providerWebStorage.setItem(storageKeyPrefix + key, serializer(value)); + }; + + this.remove = function (key) { + providerWebStorage && providerWebStorage.removeItem(storageKeyPrefix + key); + } + + this.$get = [ + '$rootScope', + '$window', + '$log', + '$timeout', + '$document', + + function( + $rootScope, + $window, + $log, + $timeout, + $document + ){ + + // The magic number 10 is used which only works for some keyPrefixes... + // See https://github.com/gsklee/ngStorage/issues/137 + var prefixLength = storageKeyPrefix.length; + + // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app + // Note: recheck mainly for testing (so we can use $window[storageType] rather than window[storageType]) + var isSupported = isStorageSupported($window, storageType), + webStorage = isSupported || ($log.warn('This browser does not support Web Storage!'), {setItem: angular.noop, getItem: angular.noop, removeItem: angular.noop}), + $storage = { + $default: function(items) { + for (var k in items) { + angular.isDefined($storage[k]) || ($storage[k] = angular.copy(items[k]) ); + } + + $storage.$sync(); + return $storage; + }, + $reset: function(items) { + for (var k in $storage) { + '$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k)); + } + + return $storage.$default(items); + }, + $sync: function () { + for (var i = 0, l = webStorage.length, k; i < l; i++) { + // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty) + (k = webStorage.key(i)) && storageKeyPrefix === k.slice(0, prefixLength) && ($storage[k.slice(prefixLength)] = deserializer(webStorage.getItem(k))); + } + }, + $apply: function() { + var temp$storage; + + _debounce = null; + + if (!angular.equals($storage, _last$storage)) { + temp$storage = angular.copy(_last$storage); + angular.forEach($storage, function(v, k) { + if (angular.isDefined(v) && '$' !== k[0]) { + webStorage.setItem(storageKeyPrefix + k, serializer(v)); + delete temp$storage[k]; + } + }); + + for (var k in temp$storage) { + webStorage.removeItem(storageKeyPrefix + k); + } + + _last$storage = angular.copy($storage); + } + }, + $supported: function() { + return !!isSupported; + } + }, + _last$storage, + _debounce; + + $storage.$sync(); + + _last$storage = angular.copy($storage); + + $rootScope.$watch(function() { + _debounce || (_debounce = $timeout($storage.$apply, 100, false)); + }); + + // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent` + $window.addEventListener && $window.addEventListener('storage', function(event) { + if (!event.key) { + return; + } + + // Reference doc. + var doc = $document[0]; + + if ( (!doc.hasFocus || !doc.hasFocus()) && storageKeyPrefix === event.key.slice(0, prefixLength) ) { + event.newValue ? $storage[event.key.slice(prefixLength)] = deserializer(event.newValue) : delete $storage[event.key.slice(prefixLength)]; + + _last$storage = angular.copy($storage); + + $rootScope.$apply(); + } + }); + + $window.addEventListener && $window.addEventListener('beforeunload', function() { + $storage.$apply(); + }); + + return $storage; + } + ]; + }; + } + +})); diff --git a/www/assets/library/ngStorage-0.3.11.min.js b/www/assets/library/ngStorage-0.3.11.min.js new file mode 100644 index 0000000..a8a4310 --- /dev/null +++ b/www/assets/library/ngStorage-0.3.11.min.js @@ -0,0 +1 @@ +/*! ngstorage 0.3.10 | Copyright (c) 2016 Gias Kay Lee | MIT License */!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):a.hasOwnProperty("angular")?b(a.angular):"object"==typeof exports&&(module.exports=b(require("angular")))}(this,function(a){"use strict";function b(a,b){var c;try{c=a[b]}catch(d){c=!1}if(c){var e="__"+Math.round(1e7*Math.random());try{a[b].setItem(e,e),a[b].removeItem(e,e)}catch(d){c=!1}}return c}function c(c){var d=b(window,c);return function(){var e="ngStorage-";this.setKeyPrefix=function(a){if("string"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setKeyPrefix() expects a String.");e=a};var f=a.toJson,g=a.fromJson;this.setSerializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setSerializer expects a function.");f=a},this.setDeserializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setDeserializer expects a function.");g=a},this.supported=function(){return!!d},this.get=function(a){return d&&g(d.getItem(e+a))},this.set=function(a,b){return d&&d.setItem(e+a,f(b))},this.remove=function(a){d&&d.removeItem(e+a)},this.$get=["$rootScope","$window","$log","$timeout","$document",function(d,h,i,j,k){var l,m,n=e.length,o=b(h,c),p=o||(i.warn("This browser does not support Web Storage!"),{setItem:a.noop,getItem:a.noop,removeItem:a.noop}),q={$default:function(b){for(var c in b)a.isDefined(q[c])||(q[c]=a.copy(b[c]));return q.$sync(),q},$reset:function(a){for(var b in q)"$"===b[0]||delete q[b]&&p.removeItem(e+b);return q.$default(a)},$sync:function(){for(var a,b=0,c=p.length;c>b;b++)(a=p.key(b))&&e===a.slice(0,n)&&(q[a.slice(n)]=g(p.getItem(a)))},$apply:function(){var b;if(m=null,!a.equals(q,l)){b=a.copy(l),a.forEach(q,function(c,d){a.isDefined(c)&&"$"!==d[0]&&(p.setItem(e+d,f(c)),delete b[d])});for(var c in b)p.removeItem(e+c);l=a.copy(q)}},$supported:function(){return!!o}};return q.$sync(),l=a.copy(q),d.$watch(function(){m||(m=j(q.$apply,100,!1))}),h.addEventListener&&h.addEventListener("storage",function(b){if(b.key){var c=k[0];c.hasFocus&&c.hasFocus()||e!==b.key.slice(0,n)||(b.newValue?q[b.key.slice(n)]=g(b.newValue):delete q[b.key.slice(n)],l=a.copy(q),d.$apply())}}),h.addEventListener&&h.addEventListener("beforeunload",function(){q.$apply()}),q}]}}return a=a&&a.module?a:window.angular,a.module("ngStorage",[]).provider("$localStorage",c("localStorage")).provider("$sessionStorage",c("sessionStorage"))}); diff --git a/www/index.html b/www/index.html index 1acd4fc..da030c4 100755 --- a/www/index.html +++ b/www/index.html @@ -42,14 +42,14 @@ Naranawm @@ -62,15 +62,23 @@ + - - - - + + + + + + + + + + + diff --git a/www/kelku/kelku.html b/www/kelku/kelku.html new file mode 100644 index 0000000..0e632dc --- /dev/null +++ b/www/kelku/kelku.html @@ -0,0 +1,26 @@ + +
+

Zola'u Nìprrte'

+

Kaltxì and Welcome! Naranawm was built by the community of Learn Na'vi to manage the ever growing Na'vi language created by Dr. Paul Frommer for the movie Avatar. Naranawm is a free and open source project that was designed from the ground up to support the creation and management of languages and their translations into other languages.

+

Get started today

+
+ + +
+
+

Safari bug warning!

+

As of v9.1.2, Safari exhibits a bug in which resizing your browser horizontally causes rendering errors in the justified nav that are cleared upon refreshing.

+

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

+

View details »

+
+
+

Heading

+

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

+

View details »

+
+
+

Heading

+

Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa.

+

View details »

+
+
\ No newline at end of file diff --git a/www/kelku/kelku.js b/www/kelku/kelku.js new file mode 100644 index 0000000..fb96b27 --- /dev/null +++ b/www/kelku/kelku.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('naranawm') + // Home Controller + .controller('KelkuCtrl', ["$scope", function($scope) { + + }]); diff --git a/www/kelku/login.js b/www/kelku/login.js new file mode 100644 index 0000000..d2c5d35 --- /dev/null +++ b/www/kelku/login.js @@ -0,0 +1,20 @@ +'use strict'; + +angular.module('naranawm') + .controller('LoginCtrl', ["Auth", "$scope", function(Auth, $scope) { + $scope.form = {}; + + $scope.login = function(){ + Auth.login($scope.form, function(){ + // Success + console.log("Success"); + }, function(error){ + // Error + console.log(error); + }); + }; + + $scope.showLoginForm = function(){ + return $scope.user.id === undefined; + } + }]); diff --git a/www/languages/languages.js b/www/languages/languages.js deleted file mode 100644 index bf5d28a..0000000 --- a/www/languages/languages.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -angular.module('naranawm.languages', ['ngRoute']) - - .config(['$routeProvider', function($routeProvider) { - $routeProvider.when('/languages', { - templateUrl: 'languages/languages.html', - controller: 'LanguagesCtrl' - }); - }]) - - .controller('LanguagesCtrl', ["$scope", "Models", function($scope, Models) { - $scope.languages = Models.query({model: "Language"}, function() { - - }); //query() returns all the entries - }]); diff --git a/www/services/auth.js b/www/services/auth.js new file mode 100644 index 0000000..8d7d77b --- /dev/null +++ b/www/services/auth.js @@ -0,0 +1,54 @@ + +angular.module('naranawm').factory('Auth', ['$rootScope', '$http', '$localStorage', '$location', '$q', function ($rootScope, $http, $localStorage, $location, $q) { + + function changeUser(user) { + angular.extend($rootScope.user, user); + } + + function urlBase64Decode(str) { + let output = str.replace('-', '+').replace('_', '/'); + switch (output.length % 4) { + case 0: + break; + case 2: + output += '=='; + break; + case 3: + output += '='; + break; + default: + throw 'Illegal base64url string!'; + } + return window.atob(output); + } + + function getUserFromToken() { + const token = $localStorage.token; + let user = {}; + if (typeof token !== 'undefined') { + const encoded = token.split('.')[1]; + user = JSON.parse(urlBase64Decode(encoded)); + } + return user; + } + + $rootScope.user = getUserFromToken(); + + return { + login: function(data, success, error){ + "use strict"; + $http.post("/login", data).then(function(res){ + $localStorage.token = res.data.token; + $rootScope.user = getUserFromToken(); + $location.path("/vezeyko"); + success(); + }, error); + }, + logout: function(success){ + "use strict"; + changeUser({}); + delete $localStorage.token; + success(); + } + }; +}]); \ No newline at end of file diff --git a/www/services/interceptors.js b/www/services/interceptors.js new file mode 100644 index 0000000..77df30f --- /dev/null +++ b/www/services/interceptors.js @@ -0,0 +1,19 @@ + +angular.module('naranawm').factory('Interceptors', ['$localStorage', '$location', '$q', function ($localStorage, $location, $q) { + + return { + 'request': function (config) { + config.headers = config.headers || {}; + if ($localStorage.token) { + config.headers.Authorization = 'JWT ' + $localStorage.token; + } + return config; + }, + 'responseError': function(response) { + if(response.status === 401 || response.status === 403) { + $location.path('/signin'); + } + return $q.reject(response); + } + }; +}]); \ No newline at end of file diff --git a/www/sources/sources.js b/www/sources/sources.js deleted file mode 100644 index aab97da..0000000 --- a/www/sources/sources.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -angular.module('naranawm.sources', ['ngRoute']) - - .config(['$routeProvider', function($routeProvider) { - $routeProvider.when('/sources', { - templateUrl: 'sources/sources.html', - controller: 'SourcesCtrl' - }); - }]) - - .controller('SourcesCtrl', ["$scope", "Models", function($scope, Models) { - $scope.sources = Models.query({model: "Source"}, function() { - - }); //query() returns all the entries - }]); diff --git a/www/vezeyko/dashboard.html b/www/vezeyko/dashboard.html new file mode 100644 index 0000000..a46162e --- /dev/null +++ b/www/vezeyko/dashboard.html @@ -0,0 +1,2 @@ +{{ language }} + \ No newline at end of file diff --git a/www/vezeyko/dashboard.js b/www/vezeyko/dashboard.js new file mode 100644 index 0000000..99925e9 --- /dev/null +++ b/www/vezeyko/dashboard.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('naranawm.vezeyko') + .controller('DashboardCtrl', ["$scope", "$routeParams", function($scope, $routeParams) { + $scope.language = $routeParams.language; + }]); diff --git a/www/languages/languages.html b/www/vezeyko/languages.html similarity index 100% rename from www/languages/languages.html rename to www/vezeyko/languages.html diff --git a/www/vezeyko/languages.js b/www/vezeyko/languages.js new file mode 100644 index 0000000..40c4bfc --- /dev/null +++ b/www/vezeyko/languages.js @@ -0,0 +1,8 @@ +'use strict'; + +angular.module('naranawm.vezeyko') + .controller('LanguagesCtrl', ["$scope", "Models", function($scope, Models) { + $scope.languages = Models.query({model: "Language"}, function() { + + }); //query() returns all the entries + }]); diff --git a/www/vezeyko/ralpeng.js b/www/vezeyko/ralpeng.js new file mode 100644 index 0000000..e69de29 diff --git a/www/sources/sources.html b/www/vezeyko/sources.html similarity index 100% rename from www/sources/sources.html rename to www/vezeyko/sources.html diff --git a/www/vezeyko/sources.js b/www/vezeyko/sources.js new file mode 100644 index 0000000..3a989b0 --- /dev/null +++ b/www/vezeyko/sources.js @@ -0,0 +1,8 @@ +'use strict'; + +angular.module('naranawm.vezeyko') + .controller('SourcesCtrl', ["$scope", "Models", function($scope, Models) { + $scope.sources = Models.query({model: "Source"}, function() { + + }); //query() returns all the entries + }]); diff --git a/www/vezeyko/vezeyko.html b/www/vezeyko/vezeyko.html new file mode 100644 index 0000000..8318c86 --- /dev/null +++ b/www/vezeyko/vezeyko.html @@ -0,0 +1 @@ +Test \ No newline at end of file diff --git a/www/vezeyko/vezeyko.js b/www/vezeyko/vezeyko.js new file mode 100644 index 0000000..30c9912 --- /dev/null +++ b/www/vezeyko/vezeyko.js @@ -0,0 +1,36 @@ +'use strict'; +// Vezeyko: Put in order / be organized +// This is the dictionary management section of Naranawm +angular.module('naranawm.vezeyko', ['ngRoute']) + .config(['$routeProvider', function($routeProvider){ + + $routeProvider.when('/vezeyko', { + templateUrl: 'vezeyko/vezeyko.html', + controller: 'VezeykoCtrl' + }); + + $routeProvider.when('/vezeyko/:language', { + templateUrl: 'vezeyko/dashboard.html', + controller: 'DashboardCtrl' + }); + + $routeProvider.when('/vezeyko/:language/languages', { + templateUrl: 'vezeyko/languages.html', + controller: 'LanguagesCtrl' + }); + + $routeProvider.when('/vezeyko/:language/sources', { + templateUrl: 'vezeyko/sources.html', + controller: 'SourcesCtrl' + }); + }]) + .controller('VezeykoCtrl', ["$scope", "$location", function($scope, $location) { + + // TODO: Hook this up to a list of languages that we support + // If only 1 language defined, go ahead and switch to that language + $scope.languages = ["nav"]; + console.log("Test"); + if($scope.languages.length === 1){ + $location.path('/vezeyko/' + $scope.languages[0]); + } + }]);