This commit is contained in:
KimLS 2017-01-22 22:26:34 -08:00
parent cd4d79c02b
commit 40edbc3b64
48 changed files with 82834 additions and 9 deletions

View File

@ -132,7 +132,7 @@ void EQApplicationPacket::build_header_dump(char *buffer) const
#ifdef STATIC_OPCODE
sprintf(buffer, "[OpCode 0x%04x Size=%u]\n", emu_opcode,size);
#else
sprintf(buffer, "[OpCode %s Size=%u]",OpcodeManager::EmuToName(emu_opcode),size);
sprintf(buffer, "[OpCode %s(0x%04x) Size=%u]",OpcodeManager::EmuToName(emu_opcode), GetProtocolOpcode(), size);
#endif
}

View File

@ -118,8 +118,11 @@ public:
uint16 GetOpcodeBypass() const { return opcode_bypass; }
void SetOpcodeBypass(uint16 v) { opcode_bypass = v; }
uint16 GetProtocolOpcode() const { return protocol_opcode; }
void SetProtocolOpcode(uint16 v) { protocol_opcode = v; }
protected:
uint16 protocol_opcode;
uint8 app_opcode_size;
uint16 opcode_bypass;
private:

View File

@ -120,6 +120,7 @@ EQApplicationPacket *EQ::Net::EQStream::PopPacket() {
EmuOpcode emu_op = (*m_opcode_manager)->EQToEmu(opcode);
EQApplicationPacket *ret = new EQApplicationPacket(emu_op, (unsigned char*)p->Data() + m_owner->m_options.opcode_size, p->Length() - m_owner->m_options.opcode_size);
ret->SetProtocolOpcode(opcode);
m_packet_queue.pop_front();
return ret;
}

24
wi/http/data/endpoint.js Normal file
View File

@ -0,0 +1,24 @@
var auth = require('../../core/jwt_auth.js').auth;
var sql = require('./sql.js');
var RegisterEndpoint = function(app, api, single_name, plural_name, pkey) {
app.get('/api/data/' + single_name + '/:' + pkey, auth, function (req, res) {
sql.Retrieve(req, res, plural_name, pkey);
});
app.put('/api/data/' + single_name + '/:' + pkey, auth, function (req, res) {
sql.CreateUpdate(req, res, plural_name, pkey);
});
app.delete('/api/data/' + single_name + '/:' + pkey, auth, function (req, res) {
sql.Delete(req, res, plural_name, pkey);
});
app.post('/api/data/' + single_name + '/search', auth, function (req, res) {
sql.Search(req, res, plural_name, pkey, res);
});
};
module.exports = {
'Register': RegisterEndpoint
}

9
wi/http/data/index.js Normal file
View File

@ -0,0 +1,9 @@
var endpoint = require('./endpoint.js');
var RegisterAPI = function(app, api) {
endpoint.Register(app, api, 'item', 'items', 'id');
};
module.exports = {
'Register': RegisterAPI
}

340
wi/http/data/sql.js Normal file
View File

@ -0,0 +1,340 @@
var moment = require('moment');
function CreateReplace(table, body, fields) {
try {
var query = 'REPLACE INTO ' + table + ' VALUES(';
var first = true;
var args = [];
for(var idx in fields) {
if(first) {
first = false;
} else {
query += ',';
}
query += '?';
var entry = fields[idx];
if(entry.type === 12) {
try {
var d = new moment(body[entry.name]);
if(d.isValid()) {
args.push(d.format('YYYY-MM-DD HH:mm:ss'));
} else {
args.push(null);
}
} catch(ex) {
args.push(null);
}
} else {
args.push(body[entry.name]);
}
}
query += ')';
return { 'query': query, 'args': args };
} catch(ex) {
return { 'query': '', 'args': [] };
}
}
function CreateUpdate(req, res, table, pkey) {
req.mysql.getConnection(function(err, connection) {
try {
if(err) {
console.log(err);
connection.release();
res.sendStatus(500);
return;
}
if(req.body[pkey] !== parseInt(req.params[pkey], 10)) {
connection.release();
res.sendStatus(400);
return;
}
connection.query('SELECT * FROM ' + table + ' WHERE ' + pkey + '=? LIMIT 1', [req.params[pkey]], function (error, results, fields) {
try {
if(error) {
console.log(error);
connection.release();
res.sendStatus(400);
return;
}
var replace = CreateReplace(table, req.body, fields);
if(replace.query === '') {
connection.release();
res.sendStatus(400);
return;
}
connection.query(replace.query, replace.args, function(error, results, fields) {
try {
if(error) {
console.log(error);
connection.release();
res.sendStatus(400);
return;
}
connection.release();
res.sendStatus(200);
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
}
function Retrieve(req, res, table, pkey) {
req.mysql.getConnection(function(err, connection) {
try {
if(err) {
console.log(err);
connection.release();
res.sendStatus(500);
return;
}
connection.query('SELECT * FROM ' + table + ' WHERE ' + pkey + '=? LIMIT 1', [req.params[pkey]], function (error, results, fields) {
try {
if(results.length == 0) {
connection.release();
res.sendStatus(404);
return;
}
var result = results[0];
var ret = { };
for(var idx in result) {
var value = result[idx];
ret[idx] = value;
}
connection.release();
res.json(ret);
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
}
function Delete(req, res, table, pkey) {
req.mysql.getConnection(function(err, connection) {
try {
if(err) {
console.log(err);
connection.release();
res.sendStatus(500);
return;
}
connection.query('DELETE FROM ' + table + ' WHERE ' + pkey + '=? LIMIT 1', [req.params[pkey]], function (error, results, fields) {
try {
if(error) {
console.log(error);
connection.release();
res.sendStatus(400);
return;
}
connection.release();
res.sendStatus(200);
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
}
function Search(req, res, table, pkey) {
//Verify incoming model
if(!req.body.hasOwnProperty('draw')) {
res.sendStatus(400);
return;
}
if(!req.body.hasOwnProperty('start')) {
res.sendStatus(400);
return;
}
if(!req.body.hasOwnProperty('length')) {
res.sendStatus(400);
return;
}
if(!req.body.hasOwnProperty('search')) {
res.sendStatus(400);
return;
}
if(!req.body.hasOwnProperty('columns')) {
res.sendStatus(400);
return;
}
if(!req.body.hasOwnProperty('order')) {
res.sendStatus(400);
return;
}
req.mysql.getConnection(function(err, connection) {
try {
if(err) {
console.log(err);
connection.release();
res.sendStatus(500);
return;
}
var ret = { };
ret.draw = req.body['draw'];
ret.data = [];
var query = 'SELECT ';
var idx;
var args = [];
var first = true;
for(idx in req.body['columns']) {
var column = req.body['columns'][idx];
if(first) {
first = false;
} else {
query += ', ';
}
query += connection.escapeId(column.data);
}
query += ' FROM ' + table;
first = true;
for(idx in req.body['order']) {
var order = req.body['order'][idx];
if(first) {
query += ' ORDER BY ';
first = false;
} else {
query += ', ';
}
var column = req.body['columns'][order.column];
query += connection.escapeId(column.data);
if(order.dir === 'asc') {
query += ' ASC';
} else {
query += ' DESC';
}
}
connection.query(query, args, function (error, results, fields) {
try {
if(error) {
console.log(error);
connection.release();
res.sendStatus(400);
return;
}
ret.recordsTotal = results.length;
for(var result_idx in results) {
var result = results[result_idx];
if(req.body['search'].value && req.body['search'].value.length > 0) {
var found = false;
for(idx in req.body['columns']) {
var column = req.body['columns'][idx];
if(column.searchable) {
if(String(result[column.data]).toLowerCase().includes(String(req.body['search'].value).toLowerCase())) {
found = true;
break;
}
}
}
if(found) {
var obj = { };
for(var i in result) {
var value = result[i];
obj[i] = value;
}
ret.data.push(obj);
}
} else {
var obj = { };
for(var i in result) {
var value = result[i];
obj[i] = value;
}
ret.data.push(obj);
}
}
ret.recordsFiltered = ret.data.length;
ret.data = ret.data.slice(req.body['start'], req.body['start'] + req.body['length']);
connection.release();
res.json(ret);
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
} catch(ex) {
console.log(ex);
connection.release();
res.sendStatus(500);
}
});
}
module.exports = {
'CreateUpdate': CreateUpdate,
'Retrieve': Retrieve,
'Delete': Delete,
'Search': Search,
}

9
wi/http/index.js Normal file
View File

@ -0,0 +1,9 @@
var RegisterAPI = function(app, api) {
require('./eqw.js').Register(app, api);
require('./token.js').Register(app);
require('./data').Register(app, api);
};
module.exports = {
'Register': RegisterAPI
}

View File

@ -30,6 +30,12 @@ api.Init(settings.servertalk.addr, settings.servertalk.port, false, settings.ser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
//make sure all routes can see our injected dependencies
app.use(function (req, res, next) {
req.servertalk = api;
@ -42,10 +48,8 @@ app.get('/', function (req, res) {
res.send({ status: "online" });
});
require('./http/token.js').Register(app);
require('./http/eqw.js').Register(app, api);
//require('./ws/token.js').Register(app);
require('./ws/eqw.js').Register(wsi, api);
require('./http').Register(app, api);
require('./ws').Register(wsi, api);
server.on('request', app);
server.listen(settings.port, function () { console.log('Listening on ' + server.address().port) });

View File

@ -11,15 +11,15 @@ class ServertalkAPI
var self = this;
this.client.on('connecting', function() {
console.log('Connecting...');
//console.log('Connecting...');
});
this.client.on('connect', function(){
console.log('Connected');
//console.log('Connected');
});
this.client.on('close', function(){
console.log('Closed');
//console.log('Closed');
});
this.client.on('error', function(err){

View File

@ -11,10 +11,12 @@
"dependencies": {
"body-parser": "^1.15.2",
"express": "^4.14.0",
"hammerjs": "^2.0.8",
"jsonwebtoken": "^7.2.1",
"libsodium": "^0.4.8",
"libsodium-wrappers": "^0.4.8",
"libsodium-wrappers-sumo": "^0.4.8",
"moment": "^2.17.1",
"mysql": "^2.12.0",
"node-uuid": "^1.4.7",
"ws": "^1.1.1"

47
wi/wi-front/.gitignore vendored Normal file
View File

@ -0,0 +1,47 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity

3
wi/wi-front/config.json Normal file
View File

@ -0,0 +1,3 @@
{
"api_url": "http://localhost:9080"
}

13
wi/wi-front/dist/css/app.css vendored Normal file
View File

@ -0,0 +1,13 @@
.login-panel {
width: 400px;
padding: 28px; }
.login-title {
margin: 3em 0 3em 0; }
.login-error-message {
color: #ff2828;
padding: 8px; }

9
wi/wi-front/dist/css/vendor.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
wi/wi-front/dist/index.html vendored Normal file
View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="Web portal for EQEmu WI Front">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EQEmu WI Front</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700">
<link rel="stylesheet" href="css/vendor.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<app-root>Loading...</app-root>
<script type="text/javascript" src="js/bundle.js"></script>
<script type="text/javascript" src="js/templates.js"></script>
</body>
</html>

76151
wi/wi-front/dist/js/bundle.js vendored Normal file

File diff suppressed because one or more lines are too long

1
wi/wi-front/dist/js/bundle.js.map vendored Normal file

File diff suppressed because one or more lines are too long

4
wi/wi-front/dist/js/templates.js vendored Normal file
View File

@ -0,0 +1,4 @@
angular.module('templates', []).run(['$templateCache', function($templateCache) {$templateCache.put('index.html','<!DOCTYPE html>\r\n<html>\r\n\t<head>\r\n\t\t<meta charset="utf-8">\r\n\t\t<meta http-equiv="X-UA-Compatible" content="IE=edge">\r\n\t\t<meta name="description" content="Web portal for EQEmu WI Front">\r\n\t\t<meta name="viewport" content="width=device-width, initial-scale=1">\r\n\t\t<title>EQEmu WI Front</title>\r\n\r\n\t\t<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700">\r\n <link rel="stylesheet" href="css/vendor.css">\r\n\t\t<link rel="stylesheet" href="css/app.css">\r\n\t</head>\r\n\t<body>\r\n <app-root>Loading...</app-root>\r\n\t\t<script type="text/javascript" src="js/bundle.js"></script>\r\n <script type="text/javascript" src="js/templates.js"></script>\r\n\t</body>\r\n</html>\r\n');
$templateCache.put('app/app.html','<ui-view layout-fill></ui-view>');
$templateCache.put('app/components/login/login.html','<md-content layout-fill layout="column" layout-align="start center">\r\n <h2 class="login-title">Login</h2>\r\n <form layout="column" class="md-whiteframe-4dp login-panel" ng-submit="$ctrl.doLogin()">\r\n <md-input-container class="md-block">\r\n <label>Username</label>\r\n <input name="username" required ng-model="$ctrl.username" ng-disabled="$ctrl.loggingIn" aria-label="Username">\r\n </md-input-container>\r\n\r\n <md-input-container class="md-block">\r\n <label>Password</label>\r\n <input type="password" name="password" required ng-model="$ctrl.password" ng-disabled="$ctrl.loggingIn" aria-label="Password">\r\n </md-input-container>\r\n\r\n <div layout="row">\r\n <md-checkbox ng-model="$ctrl.rememberMe" ng-disabled="$ctrl.loggingIn" aria-label="Remember Me">Remember Me</md-checkbox>\r\n </div>\r\n\r\n <span class="login-error-message" ng-show="$ctrl.error">\r\n Unauthorized login.\r\n </span>\r\n\r\n <md-button type="submit" class="md-raised md-primary" ng-disabled="!$ctrl.canLogin()">Login</md-button>\r\n </form>\r\n</md-content>\r\n');
$templateCache.put('app/components/portal/portal.html','<md-content layout-fill>\r\n <md-toolbar class="md-hue-2">\r\n <div class="md-toolbar-tools">\r\n <md-button class="md-icon-button" aria-label="menu">\r\n <md-icon class="mdi mdi-menu mdi-24px"></md-icon>\r\n </md-button>\r\n <h2>\r\n <span>EQEmu WI Portal</span>\r\n </h2>\r\n <span flex></span>\r\n <md-menu md-offset="-20 -20">\r\n <md-button class="md-icon-button" aria-label="More" ng-click="$ctrl.openMenu($mdOpenMenu, $event)">\r\n <md-icon md-menu-origin class="mdi mdi-dots-vertical mdi-24px"></md-icon>\r\n </md-button>\r\n <md-menu-content width="4">\r\n <md-menu-item>\r\n <md-button ng-click="$ctrl.logout($event)">\r\n <md-iconclass="mdi mdi-power mdi-24px" md-menu-align-target></md-icon>\r\n Logout\r\n </md-button>\r\n </md-menu-item>\r\n </md-menu-content>\r\n </md-menu>\r\n </div>\r\n </md-toolbar>\r\n</md-content>');}]);

209
wi/wi-front/gulpfile.js Normal file
View File

@ -0,0 +1,209 @@
'use strict';
var del = require('del');
var fs = require('fs');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');
var concat = require('gulp-concat');
var connect = require('gulp-connect');
var templateCache = require('gulp-angular-templatecache');
var minifyCSS = require('gulp-cssnano');
var minifyHTML = require('gulp-htmlmin');
var sass = require('gulp-sass');
var gulpsync = require('gulp-sync')(gulp);
gulp.task('clean:css', function() {
return del([
'dist/css/**/*'
]);
});
gulp.task('clean:javascript', function() {
return del([
'dist/js/**/*'
]);
});
gulp.task('clean', ['clean:css', 'clean:javascript']);
gulp.task('app:javascript:prod', function () {
var b = browserify({
entries: 'src/index.js',
debug: false
});
return b.transform('babelify', {presets: ["es2015"]})
.transform('brfs')
.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify())
.on('error', gutil.log)
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./dist/js/'))
.pipe(connect.reload());
});
gulp.task('app:javascript:dev', function () {
var b = browserify({
entries: 'src/index.js',
debug: true
});
return b.transform('babelify', {presets: ["es2015"]})
.transform('brfs')
.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.on('error', gutil.log)
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./dist/js/'))
.pipe(connect.reload());
});
gulp.task('javascript:dev', ['app:javascript:dev']);
gulp.task('javascript:prod', ['app:javascript:prod']);
var vendor_css = [
'node_modules/angular-material/angular-material.min.css',
'node_modules/angular-loading-bar/build/loading-bar.min.css',
'node_modules/mdi/css/materialdesignicons.min.css'
];
gulp.task('vendor:css:prod', function() {
return gulp.src(vendor_css)
.pipe(minifyCSS())
.pipe(concat('vendor.css'))
.pipe(gulp.dest('dist/css'))
.pipe(connect.reload());
});
gulp.task('vendor:css:dev', function() {
return gulp.src(vendor_css)
.pipe(concat('vendor.css'))
.pipe(gulp.dest('dist/css'))
.pipe(connect.reload());
});
var app_sass = [
'src/**/*.scss',
];
gulp.task('app:sass:prod', function() {
return gulp.src(app_sass)
.pipe(sass().on('error', sass.logError))
.pipe(minifyCSS())
.pipe(concat('app.css'))
.pipe(gulp.dest('dist/css'))
.pipe(connect.reload());
});
gulp.task('app:sass:dev', function() {
return gulp.src(app_sass)
.pipe(sass().on('error', sass.logError))
.pipe(concat('app.css'))
.pipe(gulp.dest('dist/css'))
.pipe(connect.reload());
});
gulp.task('css:prod', ['vendor:css:prod', 'app:sass:prod']);
gulp.task('css:dev', ['vendor:css:dev', 'app:sass:dev']);
var assets_images = [
'src/assets/images/**/*',
];
var assets_media = [
'src/assets/media/*',
];
gulp.task('assets:images', function() {
return gulp.src(assets_images)
.pipe(gulp.dest('dist/images'))
.pipe(connect.reload());
});
gulp.task('assets:media', function() {
return gulp.src(assets_media)
.pipe(gulp.dest('dist/media'))
.pipe(connect.reload());
});
var assets_fonts = [
'node_modules/mdi/fonts/*'
];
gulp.task('assets:fonts', function() {
return gulp.src(assets_fonts)
.pipe(gulp.dest('dist/fonts'));
});
gulp.task('assets', ['assets:images', 'assets:media', 'assets:fonts']);
var app_html = [
'src/**/*.html'
];
gulp.task('app:html:prod', function() {
return gulp.src(app_html)
.pipe(minifyHTML({collapseWhitespace: true, removeComments: true}))
.pipe(templateCache({standalone: true}))
.pipe(gulp.dest('dist/js'))
.pipe(connect.reload());
});
gulp.task('app:html:dev', function() {
return gulp.src(app_html)
.pipe(templateCache({standalone: true}))
.pipe(gulp.dest('dist/js'))
.pipe(connect.reload());
});
var app_entry = [
'src/index.html'
];
gulp.task('app:entry:prod', function() {
return gulp.src(app_entry)
.pipe(minifyHTML({collapseWhitespace: true, removeComments: true}))
.pipe(gulp.dest('dist'))
.pipe(connect.reload());
});
gulp.task('app:entry:dev', function() {
return gulp.src(app_entry)
.pipe(gulp.dest('dist'))
.pipe(connect.reload());
});
gulp.task('build:prod', gulpsync.sync(['clean', 'javascript:prod', 'css:prod', 'app:html:prod', 'app:entry:prod', 'assets']));
gulp.task('build:dev', gulpsync.sync(['clean', 'javascript:dev', 'css:dev', 'app:html:dev', 'app:entry:dev', 'assets']));
gulp.task('build', ['build:prod']);
gulp.task('live_reload', function() {
connect.server({
livereload: true,
root: 'dist',
port: '8000'
});
});
gulp.task('watch', ['live_reload'], function() {
gulp.watch('src/**/*.js', ['app:javascript:dev']);
gulp.watch(vendor_css, ['vendor:css:dev']);
gulp.watch(app_sass, ['app:sass:dev']);
gulp.watch(assets_images, ['assets:images']);
gulp.watch(assets_media, ['assets:media']);
gulp.watch(assets_fonts, ['assets:fonts']);
gulp.watch(app_html, ['app:html:dev']);
gulp.watch(app_entry, ['app:entry:dev']);
});
gulp.task('serve', gulpsync.sync(['build:dev', 'watch']));

42
wi/wi-front/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "wi-front",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"devDependencies": {
"babel-preset-es2015": "^6.22.0",
"babelify": "^7.3.0",
"brfs": "^1.4.3",
"browserify": "^13.3.0",
"del": "^2.2.2",
"gulp": "^3.9.1",
"gulp-angular-templatecache": "^2.0.0",
"gulp-concat": "^2.6.1",
"gulp-connect": "^5.0.0",
"gulp-cssnano": "^2.1.2",
"gulp-htmlmin": "^3.0.0",
"gulp-sass": "^3.1.0",
"gulp-sourcemaps": "^2.4.0",
"gulp-sync": "^0.1.4",
"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.8",
"lodash": "^4.17.4",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
},
"dependencies": {
"angular": "^1.6.1",
"angular-animate": "^1.6.1",
"angular-aria": "^1.6.1",
"angular-loading-bar": "^0.9.0",
"angular-material": "^1.1.1",
"angular-ui-router": "^0.4.2",
"mdi": "^1.8.36",
"ngstorage": "^0.3.11"
}
}

View File

@ -0,0 +1 @@
<ui-view layout-fill></ui-view>

View File

@ -0,0 +1,15 @@
(function() {
'use strict';
var app = angular.module('app');
app.component('appRoot', {
templateUrl: 'app/app.html',
controller: ['$state', function($state) {
var self = this;
this.$onInit = function () {
};
}]
});
})();

View File

View File

@ -0,0 +1,6 @@
(function() {
'use strict';
require('./login/login.js');
require('./portal/portal.js');
})();

View File

@ -0,0 +1,24 @@
<md-content layout-fill layout="column" layout-align="start center">
<h2 class="login-title">Login</h2>
<form layout="column" class="md-whiteframe-4dp login-panel" ng-submit="$ctrl.doLogin()">
<md-input-container class="md-block">
<label>Username</label>
<input name="username" required ng-model="$ctrl.username" ng-disabled="$ctrl.loggingIn" aria-label="Username">
</md-input-container>
<md-input-container class="md-block">
<label>Password</label>
<input type="password" name="password" required ng-model="$ctrl.password" ng-disabled="$ctrl.loggingIn" aria-label="Password">
</md-input-container>
<div layout="row">
<md-checkbox ng-model="$ctrl.rememberMe" ng-disabled="$ctrl.loggingIn" aria-label="Remember Me">Remember Me</md-checkbox>
</div>
<span class="login-error-message" ng-show="$ctrl.error">
Unauthorized login.
</span>
<md-button type="submit" class="md-raised md-primary" ng-disabled="!$ctrl.canLogin()">Login</md-button>
</form>
</md-content>

View File

@ -0,0 +1,52 @@
(function() {
'use strict';
var app = angular.module('app');
app.component('login', {
templateUrl: 'app/components/login/login.html',
controller: ['$state', 'appConfig', 'loginState', '$http', function($state, appConfig, loginState, $http) {
var self = this;
self.username = '';
self.password = '';
self.rememberMe = false;
self.doLogin = doLogin;
self.canLogin = canLogin;
self.loggingIn = false;
self.error = false;
function doLogin() {
if(!canLogin()) {
return;
}
self.loggingIn = true;
self.error = false;
var data = { username: self.username, password: self.password };
$http.post(appConfig.config.api_url + '/api/token', data)
.then(function(response) {
self.loggingIn = false;
loginState.login(self.username, response.data, self.rememberMe);
}, function(response) {
self.loggingIn = false;
self.error = true;
});
}
function canLogin() {
if(self.username.length === 0 || self.password.length === 0 || self.loggingIn) {
return false;
}
return true;
}
self.$onInit = function () {
//If logged in just goto the next state
if(loginState.isLoggedIn()) {
$state.go(loginState.nextState, loginState.nextStateParams);
};
};
}]
});
})();

View File

@ -0,0 +1,16 @@
.login-panel
{
width: 400px;
padding: 28px;
}
.login-title
{
margin: 3em 0 3em 0;
}
.login-error-message
{
color: rgba(255, 40, 40, 200);
padding: 8px;
}

View File

@ -0,0 +1,26 @@
<md-content layout-fill>
<md-toolbar class="md-hue-2">
<div class="md-toolbar-tools">
<md-button class="md-icon-button" aria-label="menu">
<md-icon class="mdi mdi-menu mdi-24px"></md-icon>
</md-button>
<h2>
<span>EQEmu WI Portal</span>
</h2>
<span flex></span>
<md-menu md-offset="-20 -20">
<md-button class="md-icon-button" aria-label="More" ng-click="$ctrl.openMenu($mdOpenMenu, $event)">
<md-icon md-menu-origin class="mdi mdi-dots-vertical mdi-24px"></md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button ng-click="$ctrl.logout($event)">
<md-iconclass="mdi mdi-power mdi-24px" md-menu-align-target></md-icon>
Logout
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
</md-content>

View File

@ -0,0 +1,39 @@
(function() {
'use strict';
var app = angular.module('app');
app.component('portal', {
templateUrl: 'app/components/portal/portal.html',
controller: ['$state', '$mdDialog', 'loginState', function($state, $mdDialog, loginState) {
var self = this;
self.openMenu = openMenu;
self.logout = logout;
function openMenu($mdOpenMenu, ev) {
self.originatorEv = ev;
$mdOpenMenu(ev);
};
function logout(ev) {
var confirm = $mdDialog.confirm()
.title('Logout')
.textContent('Are you sure you wish to logout?')
.ariaLabel('Logout')
.targetEvent(ev)
.ok('Logout')
.cancel('Cancel');
$mdDialog.show(confirm).then(function() {
loginState.logout();
$state.go('login');
}, function() {
});
}
self.$onInit = function () {
};
}]
});
})();

View File

@ -0,0 +1,24 @@
(function(){
'use strict';
var app = angular.module('app');
app.factory('applyAuthIntercept', ['$injector', function($injector) {
return {
request: function(config) {
if(config.anonOnly) {
return config;
}
if(!config.headers) {
config.headers = { };
}
var loginState = $injector.get('loginState');
config.headers.Authorization = 'Bearer ' + loginState.token;
return config;
}
};
}]);
})();

View File

@ -0,0 +1,25 @@
(function(){
'use strict';
var app = angular.module('app');
app.factory('checkAuthIntercept', ['$q', '$injector', function($q, $injector) {
return {
responseError: function(response) {
if(response.status === 401) {
var $state = $injector.get('$state');
var loginState = $injector.get('loginState');
if($state.current.name !== 'login') {
loginState.nextState = $state.current.name;
loginState.nextStateParams = {};
loginState.logout();
$state.go('login');
}
}
return $q.reject(response);
}
};
}]);
})();

View File

@ -0,0 +1,6 @@
(function() {
'use strict';
require('./apply-auth.js');
require('./check-auth.js');
})();

View File

@ -0,0 +1,21 @@
(function() {
'use strict';
var app = angular.module('app');
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
$stateProvider.state('login', {
url: '/login',
template: '<login></login>'
});
$stateProvider.state('portal', {
url: '/portal',
template: '<portal></portal>'
});
}
]);
})();

View File

@ -0,0 +1,5 @@
(function() {
'use strict';
require('./core.js');
})();

View File

@ -0,0 +1,10 @@
(function() {
'use strict';
var fs = require('fs');
var app = angular.module('app');
app.service('appConfig', function() {
this.config = JSON.parse(fs.readFileSync(__dirname + '/../../../config.json', 'utf8'));
});
})();

View File

@ -0,0 +1,6 @@
(function() {
'use strict';
require('./config.js');
require('./login.js');
})();

View File

@ -0,0 +1,78 @@
(function() {
'use strict';
var app = angular.module('app');
app.service('loginState', ['$rootScope', '$http', '$httpParamSerializer', '$state', '$localStorage', '$sessionStorage',
function($rootScope, $http, $httpParamSerializer, $state, $localStorage, $sessionStorage) {
var self = this;
self.isLoggedIn = isLoggedIn;
self.logout = logout;
self.login = login;
self.nextState = 'portal';
self.nextStateParams = { };
self.token = '';
function isLoggedIn() {
var storage_bucket = null;
var token = null;
if($localStorage.token) {
storage_bucket = $localStorage;
token = $localStorage.token;
} else if($sessionStorage.token) {
storage_bucket = $sessionStorage;
token = $sessionStorage.token;
}
if(token) {
var expires = storage_bucket.expires;
var username = storage_bucket.username;
var expire_date = new Date(expires);
var current_date = new Date();
if(current_date > expire_date) {
self.logout();
} else {
self.token = token;
self.expires = expires;
self.username = username;
return true;
}
}
return false;
}
function logout() {
delete $localStorage.token;
delete $localStorage.expires;
delete $localStorage.username;
delete $sessionStorage.token;
delete $sessionStorage.expires;
delete $sessionStorage.username;
self.wasLoggedInAs = self.username;
self.username = null;
self.token = null;
self.expires = null;
}
function login(username, loginObj, rememberMe) {
self.username = username;
self.token = loginObj.token;
self.expires = loginObj.expires;
if(rememberMe) {
$localStorage.username = self.username;
$localStorage.token = self.token;
$localStorage.expires = self.expires;
} else {
$sessionStorage.username = self.username;
$sessionStorage.token = self.token;
$sessionStorage.expires = self.expires;
}
$state.go(self.nextState, self.nextStateParams);
}
}]);
})();

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="Web portal for EQEmu WI Front">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EQEmu WI Front</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700">
<link rel="stylesheet" href="css/vendor.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<app-root>Loading...</app-root>
<script type="text/javascript" src="js/bundle.js"></script>
<script type="text/javascript" src="js/templates.js"></script>
</body>
</html>

35
wi/wi-front/src/index.js Normal file
View File

@ -0,0 +1,35 @@
var angular = require('angular');
require('angular-material');
require('angular-ui-router');
require('angular-loading-bar');
require('angular-animate');
require('ngstorage');
var app = angular.module('app', ['ngMaterial', 'ui.router', 'angular-loading-bar', 'ngAnimate' ,'ngStorage', 'templates']);
app.config(['$sceDelegateProvider', 'cfpLoadingBarProvider', '$animateProvider', '$compileProvider', '$localStorageProvider', '$sessionStorageProvider', '$httpProvider',
function($sceDelegateProvider, cfpLoadingBarProvider, $animateProvider, $compileProvider, $localStorageProvider, $sessionStorageProvider, $httpProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
'self'
]);
$animateProvider.classNameFilter(/^((?!(fa-spinner|fa-cog|fa-refresh|fa-circle-o-notch)).)*$/);
$compileProvider.preAssignBindingsEnabled(true);
$localStorageProvider.setKeyPrefix('eqemu_wi_');
$sessionStorageProvider.setKeyPrefix('eqemu_wi_');
$httpProvider.interceptors.push('applyAuthIntercept');
$httpProvider.interceptors.push('checkAuthIntercept');
}]);
angular.element(function() {
angular.bootstrap(document, ['app']);
});
require('./app/app.js');
require('./app/services');
require('./app/routes');
require('./app/components');
require('./app/interceptors');

View File

7
wi/ws/index.js Normal file
View File

@ -0,0 +1,7 @@
var RegisterAPI = function(wsi, api) {
require('./eqw.js').Register(wsi, api);
};
module.exports = {
'Register': RegisterAPI
}

View File

@ -466,7 +466,7 @@ int Client::HandlePacket(const EQApplicationPacket *app)
args.push_back(const_cast<EQApplicationPacket*>(app));
parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 0, &args);
if (Log.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1){
if (Log.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1) {
char buffer[64];
app->build_header_dump(buffer);
Log.Out(Logs::General, Logs::Client_Server_Packet_Unhandled, "%s %s", buffer, DumpPacketToString(app).c_str());