@@ -1,9 +1,9 @@ | |||
node_modules/ | |||
public/libs/ | |||
public/js | |||
public/css | |||
.idea/ | |||
.awcache/ | |||
uploads/ | |||
package-lock.json | |||
jwt.pem |
@@ -0,0 +1,26 @@ | |||
# Shimapan | |||
Shimapan is an easy to use file sharing website. It's easy to integrate shimapan into | |||
most 3rd party utilities with the use of its RESTful API. The web panel gives users the | |||
means to manage their uploaded content and various other useful utilities. | |||
## Installation | |||
Clone the latest release locally: | |||
```bash | |||
$ git clone https://github.com/Foltik/Shimapan | |||
``` | |||
Next, install dependencies and build into `public/` with `npm`: | |||
```bash | |||
$ npm install | |||
$ npm run build | |||
``` | |||
Finally, start the Node.js server with: | |||
```bash | |||
$ npm start | |||
``` | |||
Or optionally, monitor the source files, rebuilding and restarting the server | |||
whenever changes are made with `nodemon` for easy live development: | |||
```bash | |||
$ npm run watch | |||
``` |
@@ -0,0 +1,22 @@ | |||
var mongoose = require('mongoose'); | |||
var KeySchema = mongoose.Schema({ | |||
key: String, | |||
identifier: { | |||
type: String, | |||
required: true | |||
}, | |||
scope: [String], | |||
uploadCount: { | |||
type: Number, | |||
default: 0 | |||
}, | |||
uploadSize: { | |||
type: Number, | |||
default: 0 | |||
}, | |||
username: String, | |||
date: Date | |||
}); | |||
module.exports = mongoose.model('Key', KeySchema); |
@@ -1,30 +0,0 @@ | |||
var fs = require('fs'); | |||
var path = require('path'); | |||
var mongoose = require('mongoose'); | |||
var jwt = require('jsonwebtoken'); | |||
var jwtsign = require('jwt-sign'); | |||
var TokenSchema = mongoose.Schema({ | |||
scope: [String], | |||
issuer: String, | |||
issued: Date, | |||
exp: Date | |||
}); | |||
TokenSchema.methods.genJwt = function(expiry) { | |||
var exp = new Date(); | |||
exp.setDate(exp.getDate() + expiry); | |||
var payload = { | |||
_id: this._id, | |||
username: this.username, | |||
scope: this.scope, | |||
exp: parseInt(exp.getTime() / 1000) | |||
}; | |||
var key = fs.readFilySync(path.join(__dirname, '../../jwt.pem'), 'utf8'); | |||
return jwt.sign(payload, key); | |||
}; | |||
module.exports = mongoose.model('Token', TokenSchema); |
@@ -1,9 +1,5 @@ | |||
var fs = require('fs'); | |||
var path = require('path'); | |||
var mongoose = require('mongoose'); | |||
var crypto = require('crypto'); | |||
var jwt = require('jsonwebtoken'); | |||
var jwtsign = require('jwt-sign'); | |||
var passportLocalMongoose = require('passport-local-mongoose'); | |||
var UserSchema = mongoose.Schema({ | |||
username: { | |||
@@ -11,6 +7,7 @@ var UserSchema = mongoose.Schema({ | |||
unique: true, | |||
required: true | |||
}, | |||
scope: [String], | |||
uploadCount: { | |||
type: Number, | |||
default: 0 | |||
@@ -19,36 +16,9 @@ var UserSchema = mongoose.Schema({ | |||
type: Number, | |||
default: 0 | |||
}, | |||
scope: [String], | |||
hash: String, | |||
salt: String, | |||
date: Date | |||
}); | |||
UserSchema.methods.setPassword = function(password) { | |||
this.salt = crypto.randomBytes(16).toString('hex'); | |||
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha256').toString('hex'); | |||
}; | |||
UserSchema.methods.validatePassword = function(password) { | |||
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha256').toString('hex'); | |||
return this.hash === hash; | |||
}; | |||
UserSchema.methods.genJwt = function() { | |||
var expiry = new Date(); | |||
expiry.setDate(expiry.getDate() + 7); | |||
var payload = { | |||
_id: this._id, | |||
username: this.username, | |||
scope: this.scope, | |||
exp: parseInt(expiry.getTime() / 1000) | |||
}; | |||
var key = fs.readFileSync(path.join(__dirname, '../../jwt.pem'), 'utf8'); | |||
return jwt.sign(payload, key); | |||
}; | |||
UserSchema.plugin(passportLocalMongoose); | |||
module.exports = mongoose.model('User', UserSchema); |
@@ -0,0 +1 @@ | |||
body{background-color:#000;margin:0;color:#d3d3d3;font-family:Roboto,sans-serif}::-moz-focus-inner{border:0}.form{margin:50px auto;width:300px}.form h3{background-color:#000;border:2px solid #2a9fd6;border-radius:5px 5px 0 0;color:#eee;font-size:18px;padding:20px;margin:0;text-align:center;text-transform:uppercase}fieldset{border:2px solid #2a9fd6;border-top:none;margin:0;background:#000;border-radius:0 0 5px 5px;padding:20px;position:relative}fieldset:before{border-bottom:2px solid #2a9fd6;border-right:2px solid #2a9fd6;background-color:#000;content:"";width:8px;height:8px;left:50%;margin:-4px 0 0 -4px;position:absolute;top:0;transform:rotate(45deg) translateY(-2px)}.form input{display:block;font-size:14px;background:#000;color:#d3d3d3;border:1px solid #999;width:226px;padding:12px 12px;margin:auto;margin-bottom:5px}button{background:#000;border:2px solid #2a9fd6;border-radius:4px;color:#d3d3d3;cursor:pointer;display:block;padding:10px 30px;margin:auto;margin-top:20px;transition:background .25s}button:hover{background:#2a9fd6;color:#fff;text-decoration:none;outline:0} |
@@ -0,0 +1 @@ | |||
body{background-color:#060606;color:#d3d3d3;font-family:Roboto,sans-serif;font-size:14px;height:100%;line-height:20px;margin:0;padding-top:20px;padding-bottom:40px}#container{max-width:700px;text-align:center;display:flex;flex-direction:column;margin:auto}a{color:#0078b4;text-decoration:none;transition:color .25s}a:active,a:focus,a:hover{color:#005580}h1{font-size:72px;font-weight:700}.upload{margin:60px 0}.btn{font-family:Roboto,sans-serif;background:#000;border:2px solid #2a9fd6;border-radius:4px;color:#d3d3d3;cursor:pointer;display:inline-block;font-size:24px;padding:28px 48px;transition:background-color .25s,width .5s,height .5s}.btn:hover{background-color:#2a9fd6;color:#fff;text-decoration:none;outline:0}:focus{outline:0}::-moz-focus-inner{border:0}#upload-filelist{list-style-type:none;margin:20px 50px;padding:0;text-align:left}#upload-filelist>li{margin-top:5px;overflow:hidden;display:flex}.list-name{margin-top:6px;overflow:hidden;max-width:70%;text-overflow:ellipsis;white-space:nowrap}.list-url{font-family:'Roboto Mono',monospace;margin-left:auto}.list-progress{margin:10px 30px 10px 30px;flex-grow:2}.list-url a{vertical-align:super;color:#5c5c5c}.list-url a:hover{color:#d3d3d3}.list-url-clipboard-btn{border:2px solid #222;height:32px;margin-left:5px;border-radius:3px;cursor:pointer;background-color:#5c5c5c;transition:background-color .25s}.list-url-clipboard-btn:focus,.list-url-clipboard-btn:hover{background-color:#ababab;outline:0}.list-err{font-family:'Roboto Mono',monospace;margin-left:auto;color:#f66;vertical-align:super}nav a,nav>ul{color:#32809f;list-style:none;margin:0;padding:0;text-align:center}nav>ul>li{display:inline-block;margin:0;padding:0;cursor:default}nav>ul>li:after{content:"|";margin:0 8px;opacity:.3}nav>ul>li:last-child:after{content:"";margin:0} |
@@ -0,0 +1 @@ | |||
*{margin:0}body{background:#000}a{position:absolute;top:5px;left:48%;opacity:.1;height:30px;width:30px;z-index:100;cursor:default}a img{width:30px;height:30px}canvas{position:absolute;top:0;left:0}video{position:fixed;top:50%;left:50%;min-width:100%;min-height:100%;width:1920px;height:760px;z-index:-100;transform:translateX(-50%) translateY(-50%);background-size:cover;transition:1s opacity}#buffer{display:none} |
@@ -0,0 +1,234 @@ | |||
@import url('https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'); | |||
@import url('https://fonts.googleapis.com/css?family=Roboto'); | |||
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono'); | |||
*, *:before, *:after { | |||
margin: 0; | |||
padding: 0; | |||
box-sizing: border-box; | |||
} | |||
html, body { | |||
height: 100vh; | |||
} | |||
body { | |||
font: 600 14px 'Roboto', sans-serif; | |||
color: #000000; | |||
background: #000000; | |||
overflow-x: hidden; | |||
} | |||
.container { | |||
display: flex; | |||
min-height: 100%; | |||
} | |||
.sidebar { | |||
position: absolute; | |||
width: 220px; | |||
} | |||
.content { | |||
flex: 1; | |||
padding: 30px; | |||
background: #444444; | |||
box-shadow: 0 0 5px rgba(0,0,0,1); | |||
transform: translate3d(0,0,0); | |||
transition: transform .3s; | |||
color: #cccccc; | |||
} | |||
.content.isOpen { | |||
transform: translate3d(220px,0,0); | |||
} | |||
.button { | |||
position: relative; | |||
width: 120px; | |||
height: 40px; | |||
cursor: pointer; | |||
margin: 10px 0 40px 0; | |||
padding: 15px; | |||
transition-property: opacity, filter; | |||
transition-duration: .15s; | |||
text-transform: none; | |||
} | |||
.button-inner { | |||
left: 0; | |||
top: 50%; | |||
display: inline-block; | |||
margin-top: -2px; | |||
} | |||
.button-inner, | |||
.button-inner::after, | |||
.button-inner::before { | |||
background-color: #fff; | |||
position: absolute; | |||
width: 40px; | |||
height: 4px; | |||
transition: transform 0.15s ease; | |||
border-radius: 4px; | |||
} | |||
.button-inner::before, | |||
.button-inner::after { | |||
content: ""; | |||
display: block; | |||
} | |||
.button-inner::before { | |||
top: -10px; | |||
} | |||
.button-inner::after { | |||
bottom: -10px; | |||
} | |||
.button.isOpen .button-inner::before { | |||
transform: translate3d(-8px, 0, 0) rotate(-45deg) scale(0.7, 1); | |||
} | |||
.button.isOpen .button-inner::after { | |||
transform: translate3d(-8px, 0, 0) rotate(45deg) scale(0.7, 1); | |||
} | |||
.button-label { | |||
position: absolute; | |||
top: 6px; | |||
left: 50px; | |||
font-size: 24px; | |||
color: #fff; | |||
} | |||
.sidebar-title { | |||
color: #2a9fd6; | |||
cursor: pointer; | |||
font-size: 16px; | |||
line-height: 50px; | |||
text-align: center; | |||
text-transform: uppercase; | |||
letter-spacing: 7px; | |||
border-bottom: 1px solid #222; | |||
background: #2a2a2a; | |||
} | |||
.nav li { | |||
cursor: pointer; | |||
} | |||
.nav li a { | |||
outline: 0; | |||
position: relative; | |||
display: block; | |||
padding: 20px 0 20px 50px; | |||
font-size: 14px; | |||
color: #eee; | |||
background: #333333; | |||
border-bottom: 1px solid #222; | |||
text-decoration: none; | |||
} | |||
.nav li a:before { | |||
font: 14px fontawesome; | |||
position: absolute; | |||
top: 19px; | |||
left: 20px; | |||
} | |||
.nav li:nth-child(1) a:before { content: '\f00a'; } | |||
.nav li:nth-child(2) a:before { content: '\f002'; } | |||
.nav li:nth-child(3) a:before { content: '\f084'; } | |||
.nav li:nth-child(4) a:before { content: '\f0e0'; } | |||
.nav li:nth-child(5) a:before { content: '\f0c0'; } | |||
.nav li:nth-child(6) a:before { content: '\f233'; } | |||
.nav li:nth-child(7) a:before { content: '\f023'; left: 23px; } | |||
.nav li a:hover { | |||
background: #444; | |||
} | |||
::-moz-focus-inner {border: 0;} | |||
.nav li a.active { | |||
box-shadow: inset 5px 0 0 #2A9FD6, inset 6px 0 0 #222; | |||
background: #444; | |||
} | |||
.inner { | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.keys { | |||
display: flex; | |||
flex-direction: row; | |||
flex-wrap: wrap; | |||
} | |||
.key { | |||
background: #555; | |||
border: 3px solid #2a9fd6; | |||
border-radius: 5px; | |||
box-shadow: 5px 5px 10px #000; | |||
margin: 20px; | |||
width: 300px; | |||
height: 100px; | |||
cursor: pointer; | |||
display: flex; | |||
justify-content: flex-start; | |||
flex-direction: row; | |||
transition: box-shadow 0.2s; | |||
} | |||
.key:hover { | |||
box-shadow: 0 0 10px #eee; | |||
} | |||
.key i { | |||
font-size: 48px; | |||
margin: auto 0 auto 20px; | |||
} | |||
.key span { | |||
font-size: 16px; | |||
vertical-align: center; | |||
margin: auto; | |||
-moz-user-select: none; | |||
user-select: none; | |||
} | |||
.modal { | |||
display: block; | |||
position: fixed; | |||
z-index: 1; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 100%; | |||
overflow: auto; | |||
background-color: rgba(0, 0, 0, 0.4); | |||
} | |||
.modal-header { | |||
margin: 15% auto; | |||
padding: 20px; | |||
border: 1px solid #888; | |||
width: 80%; | |||
} | |||
.modal-body { | |||
margin: auto; | |||
padding: 20px; | |||
border: 1px solid #888; | |||
width: 80%; | |||
} | |||
.modal-footer { | |||
margin: auto; | |||
padding: 20px; | |||
border: 1px solid #888; | |||
width: 80%; | |||
} |
@@ -0,0 +1 @@ | |||
@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);@import url(https://fonts.googleapis.com/css?family=Roboto);@import url(https://fonts.googleapis.com/css?family=Roboto+Mono);*,:after,:before{margin:0;padding:0;box-sizing:border-box}body,html{height:100vh}body{font:600 14px Roboto,sans-serif;color:#000;background:#000;overflow-x:hidden}.container{display:flex;min-height:100%}.sidebar{position:absolute;width:220px}.content{flex:1;padding:30px;background:#444;box-shadow:0 0 5px rgba(0,0,0,1);transform:translate3d(0,0,0);transition:transform .3s;color:#ccc}.content.isOpen{transform:translate3d(220px,0,0)}.button{position:relative;width:120px;height:40px;cursor:pointer;margin:10px 0 40px 0;padding:15px;transition-property:opacity,filter;transition-duration:.15s;text-transform:none}.button-inner{left:0;top:50%;display:inline-block;margin-top:-2px}.button-inner,.button-inner::after,.button-inner::before{background-color:#fff;position:absolute;width:40px;height:4px;transition:transform .15s ease;border-radius:4px}.button-inner::after,.button-inner::before{content:"";display:block}.button-inner::before{top:-10px}.button-inner::after{bottom:-10px}.button.isOpen .button-inner::before{transform:translate3d(-8px,0,0) rotate(-45deg) scale(.7,1)}.button.isOpen .button-inner::after{transform:translate3d(-8px,0,0) rotate(45deg) scale(.7,1)}.button-label{position:absolute;top:6px;left:50px;font-size:24px;color:#fff}.sidebar-title{color:#2a9fd6;cursor:pointer;font-size:16px;line-height:50px;text-align:center;text-transform:uppercase;letter-spacing:7px;border-bottom:1px solid #222;background:#2a2a2a}.nav li a{outline:0;position:relative;display:block;padding:20px 0 20px 50px;font-size:14px;color:#eee;background:#333;border-bottom:1px solid #222;text-decoration:none}.nav li a:before{font:14px fontawesome;position:absolute;top:19px;left:20px}.nav li:nth-child(1) a:before{content:'\f00a'}.nav li:nth-child(2) a:before{content:'\f002'}.nav li:nth-child(3) a:before{content:'\f084'}.nav li:nth-child(4) a:before{content:'\f0e0'}.nav li:nth-child(5) a:before{content:'\f0c0'}.nav li:nth-child(6) a:before{content:'\f233'}.nav li:nth-child(7) a:before{content:'\f023';left:23px}.nav li a:hover{background:#444}::-moz-focus-inner{border:0}.nav li a.active{box-shadow:inset 5px 0 0 #2a9fd6,inset 6px 0 0 #222;background:#444}.inner{display:flex;flex-direction:column}.keys{display:flex;flex-direction:row;flex-wrap:wrap}.key{background:#555;border:3px solid #2a9fd6;border-radius:5px;box-shadow:5px 5px 10px #000;margin:20px;width:300px;height:100px;cursor:pointer;display:flex;justify-content:flex-start;flex-direction:row;transition:box-shadow .2s}.key:hover{box-shadow:0 0 10px #eee}.key i{font-size:48px;margin:auto 0 auto 20px}.key span{font-size:16px;vertical-align:center;margin:auto;-moz-user-select:none;user-select:none}.modal{display:block;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgba(0,0,0,.4)}.modal-header{margin:15% auto;padding:20px;border:1px solid #888;width:80%}.modal-body{margin:auto;padding:20px;border:1px solid #888;width:80%}.modal-footer{margin:auto;padding:20px;border:1px solid #888;width:80%} |
@@ -0,0 +1,10 @@ | |||
var angular = require('angular'); | |||
angular.module('ApiCtrl', ['ApiSvc', 'AuthSvc']).controller('ApiController', ['$scope', 'ApiService', 'AuthService', function($scope, ApiService, AuthService) { | |||
$scope.getKeys = function() { | |||
ApiService.getAll(function(keys) { | |||
$scope.keys = keys; | |||
}); | |||
console.log($scope.keys); | |||
}; | |||
}]); |
@@ -0,0 +1,16 @@ | |||
var angular = require('angular'); | |||
angular.module('NavCtrl', ['AuthSvc']).controller('NavController', ['$scope', '$window', 'AuthService', function($scope, $window, AuthService) { | |||
$scope.user = {}; | |||
AuthService.currentUser(function(user) { | |||
$scope.user = user; | |||
}); | |||
$scope.logout = AuthService.logout; | |||
$scope.hasPermission = function(permission) { | |||
if (!$scope.user.scope) return false; | |||
return $scope.user.scope.indexOf(permission) !== -1; | |||
}; | |||
}]); |
@@ -1,3 +1,5 @@ | |||
var angular = require('angular'); | |||
angular.module('PanelRoutes', ['ui.router']).config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) { | |||
$locationProvider.html5Mode(true); | |||
@@ -6,22 +8,22 @@ angular.module('PanelRoutes', ['ui.router']).config(['$stateProvider', '$urlRout | |||
$stateProvider | |||
.state('dashboard', { | |||
url: '/panel', | |||
templateUrl: '/views/shimapan-panel/panel-home.html' | |||
templateUrl: '/views/panel/dash.html' | |||
}).state('search', { | |||
url: '/panel/search', | |||
templateUrl: '/views/shimapan-panel/panel-search.html' | |||
templateUrl: '/views/panel/search.html' | |||
}).state('api', { | |||
url: '/panel/api', | |||
templateUrl: '/views/shimapan-panel/panel-api.html' | |||
templateUrl: '/views/panel/api.html' | |||
}).state('invites', { | |||
url: '/panel/invites', | |||
templateUrl: '/views/shimapan-panel/panel-invites.html' | |||
templateUrl: '/views/panel/invites.html' | |||
}).state('stats', { | |||
url: '/panel/stats', | |||
templateUrl: '/views/shimapan-panel/panel-stats.html' | |||
templateUrl: '/views/panel/stats.html' | |||
}).state('users', { | |||
url: '/panel/users', | |||
templateUrl: '/views/shimapan-panel/panel-users.html' | |||
templateUrl: '/views/panel/users.html' | |||
}).state('home', { | |||
onEnter: ['$window', function($window) { | |||
$window.location.href = '/home'; |
@@ -0,0 +1,8 @@ | |||
var angular = require('angular'); | |||
var uirouter = require('angular-ui-router'); | |||
var app = angular.module('shimapan-panel', ['ui.router', 'AuthSvc', 'ApiSvc', 'ApiCtrl', 'NavCtrl', 'PanelRoutes']); | |||
app.run(['$rootScope', '$state', '$stateParams', function($rootScope, $state, $stateParams) { | |||
$rootScope.$state = $state; | |||
$rootScope.$stateParams = $stateParams; | |||
}]); |
@@ -0,0 +1,22 @@ | |||
var angular = require('angular'); | |||
angular.module('ApiSvc', []).service('ApiService', ['$http', '$window', function ($http, $window) { | |||
this.getKey = function (identifier, cb) { | |||
$http({ | |||
method: 'GET', | |||
url: '/api/keys/get', | |||
params: {identifier: identifier} | |||
}).then(function (res) { | |||
cb(res.data); | |||
}); | |||
}; | |||
this.getAll = function (cb) { | |||
$http({ | |||
method: 'GET', | |||
url: '/api/keys/get' | |||
}).then(function (res) { | |||
cb(res.data); | |||
}); | |||
}; | |||
}]); |
@@ -1,28 +1,6 @@ | |||
angular.module('AuthSvc', []).service('AuthService', ['$http', '$window', function($http, $window) { | |||
function decodeToken(token) { | |||
if (token) { | |||
var payload = token.split('.')[1]; | |||
payload = $window.atob(payload); | |||
payload = JSON.parse(payload); | |||
return payload; | |||
} else { | |||
return {}; | |||
} | |||
} | |||
function saveToken(token) { | |||
$window.localStorage['shimapan-token'] = token; | |||
} | |||
function getToken() { | |||
return $window.localStorage['shimapan-token']; | |||
} | |||
this.getAuthHeader = function() { | |||
return 'Bearer ' + getToken(); | |||
}; | |||
var angular = require('angular'); | |||
angular.module('AuthSvc', []).service('AuthService', ['$http', '$window', function($http, $window) { | |||
this.login = function(user) { | |||
return $http({ | |||
method: 'POST', | |||
@@ -36,23 +14,19 @@ angular.module('AuthSvc', []).service('AuthService', ['$http', '$window', functi | |||
}, | |||
data: user | |||
}).then(function(res) { | |||
saveToken(res.data.token); | |||
if (res.status === 401) return false; | |||
$window.location.href = '/home'; | |||
}) | |||
}; | |||
this.logout = function() { | |||
$window.localStorage.removeItem('shimapan-token'); | |||
$http({ | |||
method: 'GET', | |||
url: '/api/auth/logout' | |||
}).then(function(res) { | |||
}).then(function() { | |||
$window.location.href = '/'; | |||
}); | |||
}; | |||
this.isLoggedIn = function() { | |||
var payload = decodeToken(getToken()); | |||
return payload.exp > Date.now() / 1000; | |||
}; | |||
this.register = function(user) { | |||
return $http({ | |||
@@ -67,18 +41,18 @@ angular.module('AuthSvc', []).service('AuthService', ['$http', '$window', functi | |||
}, | |||
data: user | |||
}).then(function(res) { | |||
saveToken(res.data.token); | |||
if (res.status === 401) return false; | |||
$window.location.href = '/home'; | |||
}); | |||
}; | |||
this.currentUser = function() { | |||
var payload = decodeToken(getToken()); | |||
return payload.username; | |||
}; | |||
this.currentScope = function() { | |||
var payload = decodeToken(getToken()); | |||
return payload.scope; | |||
this.currentUser = function(cb) { | |||
return $http({ | |||
method: 'GET', | |||
url: '/api/auth/session', | |||
headers: {'Content-Type': 'application/json'} | |||
}).then(function(res) { | |||
cb(res.data); | |||
}); | |||
} | |||
}]); |
@@ -1,3 +1,5 @@ | |||
var angular = require('angular'); | |||
angular.module('LoginComp', ['AuthSvc']).component('loginComponent', { | |||
templateUrl: '/views/shimapan/login-form.html', | |||
controller: ['$scope', 'AuthService', function ($scope, AuthService) { |
@@ -1,3 +1,5 @@ | |||
var angular = require('angular'); | |||
angular.module('RegisterComp', ['AuthSvc']).component('registerComponent', { | |||
templateUrl: '/views/shimapan/register-form.html', | |||
controller: ['$scope', 'AuthService', function ($scope, AuthService) { |
@@ -1,3 +1,5 @@ | |||
var angular = require('angular'); | |||
angular.module('UploadComp', ['ngFileUpload', 'AuthSvc']).component('uploadComponent', { | |||
templateUrl: '/views/shimapan/upload-form.html', | |||
controller: ['$scope', 'Upload', '$timeout', 'AuthService', function ($scope, Upload, $timeout, AuthService) { | |||
@@ -16,9 +18,6 @@ angular.module('UploadComp', ['ngFileUpload', 'AuthSvc']).component('uploadCompo | |||
file.upload = Upload.upload({ | |||
url: '/api/upload', | |||
method: 'POST', | |||
headers: { | |||
'Authorization': AuthService.getAuthHeader() | |||
}, | |||
file: file | |||
}); | |||
@@ -0,0 +1,4 @@ | |||
var angular = require('angular'); | |||
var ngFileUpload = require('ng-file-upload'); | |||
var ngclipboard = require('ngclipboard'); | |||
var app = angular.module('shimapan', ['ngFileUpload', 'ngclipboard', 'UploadComp', 'RegisterComp', 'LoginComp']); |
@@ -1,48 +0,0 @@ | |||
var index = require('./routes/index.js'); | |||
var home = require('./routes/home.js'); | |||
var upload = require('./routes/upload.js'); | |||
var view = require('./routes/view.js'); | |||
var auth = require('./routes/auth.js'); | |||
var register = require('./routes/register.js'); | |||
var login = require('./routes/login.js'); | |||
var panel = require('./routes/panel'); | |||
var fs = require('fs'); | |||
var path = require('path'); | |||
var jwt = require('express-jwt'); | |||
var jwtauth = jwt({ | |||
secret: fs.readFileSync(path.join(__dirname, '../jwt.pem'), 'utf8'), | |||
userProperty: 'payload', | |||
getToken: function(req) { | |||
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { | |||
// First check Authorization header | |||
return req.headers.authorization.split(' ')[1]; | |||
} else if (req.cookies && req.cookies['shimapan-token']) { | |||
// Get from cookies as fallback | |||
return req.cookies['shimapan-token']; | |||
} | |||
// no token received | |||
return null; | |||
} | |||
}); | |||
module.exports = function(app) { | |||
app.use('/', index); | |||
app.use('/home', jwtauth, home); | |||
app.use('/v', view); | |||
app.use('/api/upload', jwtauth, upload); | |||
app.use('/api/auth', auth); | |||
app.use('/register', register); | |||
app.use('/login', login); | |||
app.use('/panel', jwtauth, panel); | |||
app.use('/panel*', jwtauth, panel); | |||
app.use(function(err, req, res, next) { | |||
if (err.name === 'UnauthorizedError') { | |||
res.status(401); | |||
res.redirect('/login'); | |||
res.json({"message": err.name + ": " + err.message}); | |||
} | |||
}) | |||
}; |
@@ -3,25 +3,12 @@ var path = require('path'); | |||
var express = require('express'); | |||
var router = express.Router(); | |||
var async = require('async'); | |||
var User = require('../models/User.js'); | |||
var Invite = require('../models/Invite.js'); | |||
var passport = require('passport'); | |||
var striptags = require('striptags'); | |||
function checkUsername(username, callback) { | |||
if (username.length > 30) return callback(null, false); | |||
if (striptags(username) !== username) return callback(null, false); | |||
User.find({username: username}).limit(1).count(function (err, count) { | |||
if (err) return callback(err); | |||
(count === 0) ? callback(null, true) : callback(null, false); | |||
}); | |||
} | |||
function checkInvite(code, callback) { | |||
Invite.findOne({code: code}, function (err, invite) { | |||
if (err) return callback(err); | |||
@@ -33,75 +20,62 @@ function checkInvite(code, callback) { | |||
} | |||
function useInvite(code, username) { | |||
Invite.updateOne({code: code}, {recipient: username, used: new Date()}, function (err, res) { | |||
Invite.updateOne({code: code}, {recipient: username, used: new Date()}, function (err) { | |||
if (err) throw err; | |||
}); | |||
} | |||
router.post('/register', function (req, res) { | |||
// Validate the parameters | |||
async.parallel({ | |||
userCheck: function (callback) { | |||
checkUsername(req.body.username, function (err, valid) { | |||
callback(err, valid); | |||
}); | |||
}, | |||
inviteCheck: function (callback) { | |||
checkInvite(req.body.invite, function (err, valid, invite) { | |||
callback(err, {valid: valid, invite: invite}); | |||
}); | |||
} | |||
}, function (err, result) { | |||
if (!result.userCheck) { | |||
res.status(401).json({'message': 'Invalid username.'}); | |||
} else if (!result.inviteCheck.valid) { | |||
res.status(401).json({'message': 'Invalid invite code.'}); | |||
} else { | |||
useInvite(req.body.invite, req.body.username); | |||
var user = new User(); | |||
user.username = req.body.username; | |||
user.scope = result.inviteCheck.invite.scope; | |||
user.date = new Date(); | |||
user.setPassword(req.body.password); | |||
user.save(function (err) { | |||
if (err) { | |||
res.status(500).json({'message': 'Internal server error.'}); | |||
} else { | |||
res.status(200) | |||
.cookie('shimapan-token', user.genJwt(), { | |||
expires: new Date(Date.now() + 604800000), | |||
httpOnly: true | |||
}) | |||
.json({'token': user.genJwt()}); | |||
router.post('/register', function (req, res, next) { | |||
// Validate the invite code, then hand off to passport | |||
checkInvite(req.body.invite, function (err, valid, invite) { | |||
if (valid) { | |||
User.register( | |||
new User({username: req.body.username, scope: invite.scope, date: Date.now()}), | |||
req.body.password, | |||
function (err) { | |||
if (err) return res.status(403).json({'message': err.message}); | |||
passport.authenticate('local')(req, res, function () { | |||
req.session.save(function(err) { | |||
if (err) return next(err); | |||
useInvite(req.body.invite, req.body.username); | |||
res.status(200).json({'message': 'Registered.'}); | |||
}); | |||
}); | |||
} | |||
}); | |||
); | |||
} else { | |||
res.status(401).json({'message': 'Invalid invite code.'}); | |||
} | |||
}); | |||
}); | |||
router.post('/login', function (req, res) { | |||
passport.authenticate('local', function (err, user, info) { | |||
if (err) { | |||
res.status(500).json(err); | |||
} else if (user) { | |||
res.status(200) | |||
.cookie('shimapan-token', user.genJwt(), { | |||
expires: new Date(Date.now() + 604800000), | |||
httpOnly: true | |||
}) | |||
.json({'token': user.genJwt()}); | |||
} else { | |||
res.status(401).json(info); | |||
} | |||
})(req, res); | |||
router.post('/login', function (req, res, next) { | |||
passport.authenticate('local', function(err, user, info) { | |||
if (err) return next(err); | |||
if (!user) return res.status(401).json({'message': info}); | |||
req.logIn(user, function(err) { | |||
if (err) return next(err); | |||
res.status(200).json({'message': 'Logged in.'}); | |||
}); | |||
})(req, res, next); | |||
}); | |||
router.get('/logout', function(req, res) { | |||
res.clearCookie('shimapan-token'); | |||
res.status(200).json({'message': 'Successfully logged out.'}); | |||
router.get('/logout', function (req, res) { | |||
req.logout(); | |||
res.status(200).json({'message': 'Logged out.'}); | |||
}); | |||
router.get('/session', function(req, res) { | |||
if (req.session.passport.user) { | |||
User.findOne({username: req.session.passport.user}, function(err, user) { | |||
res.status(200).json({ | |||
user: user.username, | |||
scope: user.scope | |||
}); | |||
}); | |||
} else { | |||
res.status(401).json({'message': 'Unauthorized.'}); | |||
} | |||
}); | |||
module.exports = router; |
@@ -0,0 +1,67 @@ | |||
var express = require('express'); | |||
var router = express.Router(); | |||
var crypto = require('crypto'); | |||
var Key = require('../models/Key.js'); | |||
router.post('/create', function (req, res) { | |||
if (!req.body.identifier || !req.body.scope) { | |||
res.status(400).json({'message': 'Bad request.'}); | |||
return; | |||
} | |||
Key.count({'username': req.session.passport.user}, function (err, count) { | |||
if (count >= 10) { | |||
res.status(403).json({'message': 'Key limit reached.'}); | |||
return; | |||
} | |||
var scope; | |||
try { | |||
scope = JSON.parse(req.body.scope); | |||
} catch (e) { | |||
res.status(400).json({'message': e.name + ': ' + e.message}); | |||
return; | |||
} | |||
var id = req.sanitize(req.body.identifier); | |||
if (id.length === 0) id = "err"; | |||
var entry = { | |||
key: crypto.randomBytes(32).toString('hex'), | |||
identifier: id, | |||
scope: scope, | |||
username: req.session.passport.user, | |||
date: Date.now() | |||
}; | |||
Key.create(entry, function (err) { | |||
if (err) { | |||
throw err; | |||
} else { | |||
res.status(200).json({ | |||
key: entry.key, | |||
identifier: entry.identifier, | |||
scope: entry.scope | |||
}); | |||
} | |||
}) | |||
}) | |||
}); | |||
router.get('/get', function (req, res, next) { | |||
var query = {username: req.session.passport.user}; | |||
if (req.body.identifier) | |||
query.identifier = req.body.identifier; | |||
Key.find(query, function (err, keys) { | |||
if (err) { | |||
next(err); | |||
} else { | |||
res.status(200).json(keys); | |||
} | |||
}) | |||
}); | |||
module.exports = router; |
@@ -0,0 +1,39 @@ | |||
var index = require('./index.js'); | |||
var home = require('./home.js'); | |||
var upload = require('./upload.js'); | |||
var view = require('./view.js'); | |||
var auth = require('./auth.js'); | |||
var register = require('./register.js'); | |||
var login = require('./login.js'); | |||
var panel = require('./panel.js'); | |||
var keys = require('./keys.js'); | |||
var fs = require('fs'); | |||
var path = require('path'); | |||
var requireLogin = function(req, res, next) { | |||
if (!req.session.passport.user) | |||
res.redirect('/login'); | |||
else | |||
next(); | |||
}; | |||
module.exports = function(app) { | |||
app.use('/', index); | |||
app.use('/home', requireLogin, home); | |||
app.use('/v', view); | |||
app.use('/api/upload', upload); | |||
app.use('/api/auth', auth); | |||
app.use('/api/keys', requireLogin, keys); | |||
app.use('/register', register); | |||
app.use('/login', login); | |||
app.use('/panel', requireLogin, panel); | |||
app.use('/panel*', requireLogin, panel); | |||
app.use(function(err, req, res, next) { | |||
if (err.name === 'UnauthorizedError') { | |||
res.status(401); | |||
res.json({"message": err.name + ": " + err.message}); | |||
} | |||
}) | |||
}; |
@@ -14,11 +14,9 @@ function fileNameExists(name) { | |||
}); | |||
} | |||
function updateStats(user, size) { | |||
User.updateOne({username: user}, { $inc: { uploadCount: 1, uploadSize: size } }, function(err, res) { | |||
if (err) { | |||
throw err; | |||
} | |||
function updateUserStats(user, size) { | |||
User.updateOne({username: user}, {$inc: {uploadCount: 1, uploadSize: size}}, function (err, res) { | |||
if (err) throw err; | |||
}); | |||
} | |||
@@ -33,22 +31,17 @@ function genFileName() { | |||
} | |||
router.post('/', dest.single('file'), function (req, res) { | |||
if (req.payload.scope.indexOf('file.upload') === -1) { | |||
res.status(403).json({'message': 'Permission error.'}); | |||
return; | |||
} | |||
// Size must be below 128 Megabytes (1024*1024*128 Bytes) | |||
if (req.file.size >= 134217728) { | |||
res.status(413).json({'message': 'File too large.'}); | |||
return; | |||
} | |||
updateStats(req.payload.username, req.file.size); | |||
updateUserStats(req.session.passport.user, req.file.size); | |||
var entry = { | |||
name: genFileName(), | |||
uploader: req.payload.username, | |||
uploader: req.session.passport.user, | |||
created: Date.now(), | |||
file: req.file | |||
}; | |||
@@ -1,29 +1,7 @@ | |||
var passport = require('passport'); | |||
var LocalStrategy = require('passport-local').Strategy; | |||
var mongoose = require('mongoose'); | |||
var User = require('../app/models/User.js'); | |||
passport.use(new LocalStrategy({ | |||
usernameField: 'username' | |||
}, | |||
function(username, password, done) { | |||
User.findOne({username: username}, function(err, user) { | |||
if (err) return done(err); | |||
if (!user) { | |||
return done(null, false, { | |||
message: 'Invalid username.' | |||
}); | |||
} | |||
if (!user.validatePassword(password)) { | |||
return done(null, false, { | |||
message: 'Invalid password.' | |||
}); | |||
} | |||
passport.use(User.createStrategy()); | |||
passport.serializeUser(User.serializeUser()); | |||
passport.deserializeUser(User.deserializeUser()); | |||
return done(null, user); | |||
}); | |||
} | |||
)); |
@@ -1,29 +1,136 @@ | |||
var gulp = require('gulp'); | |||
var gulp = require('gulp'); | |||
var concat = require('gulp-concat'); | |||
var rename = require('gulp-rename'); | |||
var uglify = require('gulp-uglify'); | |||
var source = require('vinyl-source-stream'); | |||
var browserify = require('browserify'); | |||
var evstream = require('event-stream'); | |||
var cleanCSS = require('gulp-clean-css'); | |||
var nodemon = require('gulp-nodemon'); | |||
var path = require('path'); | |||
gulp.task('js', function() { | |||
gulp.start('shimapan'); | |||
gulp.start('shimapan-panel'); | |||
gulp.task('start', function() { | |||
nodemon({ | |||
script: 'server.js', | |||
ignore: '*.*' | |||
}); | |||
}) | |||
gulp.task('watch', function () { | |||
nodemon({ | |||
script: 'server.js', | |||
ext: 'js html css', | |||
env: {'NODE_ENV': 'dev'}, | |||
watch: [ | |||
'app/', | |||
'config/' | |||
], | |||
tasks: function (changedFiles) { | |||
var tasks = []; | |||
changedFiles.forEach(function (file) { | |||
if (path.extname(file) === '.js' && !~tasks.indexOf('minjs')) | |||
tasks.push('minjs'); | |||
if (path.extname(file) === '.css' && !~tasks.indexOf('mincss')) | |||
tasks.push('mincss'); | |||
}); | |||
return tasks; | |||
} | |||
}).on('restart?', function () { | |||
gulp.start('default'); | |||
}); | |||
}); | |||
gulp.task('default', function () { | |||
gulp.start('minjs'); | |||
gulp.start('mincss'); | |||
}); | |||
gulp.task('shimapan', function() { | |||
return gulp.src([ | |||
'public/js/services/*.js', | |||
'public/js/shimapan/**/*.js' | |||
]) | |||
.pipe(concat('shimapan.min.js')) | |||
.pipe(uglify()) | |||
.pipe(gulp.dest('public/libs/app')); | |||
gulp.task('mincss', function () { | |||
var files = [ | |||
{ | |||
src: 'app/public/css/form.css', | |||
name: 'form.min.css' | |||
}, | |||
{ | |||
src: 'app/public/css/home.css', | |||
name: 'home.min.css' | |||
}, | |||
{ | |||
src: 'app/public/css/panel.css', | |||
name: 'panel.min.css' | |||
}, | |||
{ | |||
src: 'app/public/css/index.css', | |||
name: 'index.min.css' | |||
} | |||
]; | |||
var tasks = files.map(function (entry) { | |||
return gulp.src(entry.src) | |||
.pipe(cleanCSS()) | |||
.pipe(rename(entry.name)) | |||
.pipe(gulp.dest('public/css')); | |||
}); | |||
return evstream.merge.apply(null, tasks); | |||
}) | |||
gulp.task('minjs', function () { | |||
gulp.start('concatjs'); | |||
gulp.start('browserify'); | |||
}); | |||
gulp.task('shimapan-panel', function() { | |||
return gulp.src([ | |||
'public/js/services/*.js', | |||
'public/js/shimapan-panel/**/*.js' | |||
]) | |||
.pipe(concat('shimapan-panel.min.js')) | |||
.pipe(uglify()) | |||
.pipe(gulp.dest('public/libs/app')); | |||
gulp.task('concatjs', function () { | |||
var files = [ | |||
{ | |||
src: [ | |||
'app/public/services/*.js', | |||
'app/public/panel/**/*.js' | |||
], | |||
name: 'panel.min.js' | |||
}, | |||
{ | |||
src: [ | |||
'app/public/services/*.js', | |||
'app/public/shimapan/**/*.js' | |||
], | |||
name: 'shimapan.min.js' | |||
}, | |||
{ | |||
src: 'app/public/index/*.js', | |||
name: 'index.min.js' | |||
} | |||
]; | |||
var tasks = files.map(function (entry) { | |||
return gulp.src(entry.src) | |||
.pipe(concat(entry.name)) | |||
.pipe(uglify()) | |||
.pipe(gulp.dest('public/js')); | |||
}); | |||
return evstream.merge.apply(null, tasks); | |||
}) | |||
gulp.task('browserify', ['concatjs'], function () { | |||
var files = [ | |||
{ | |||
src: 'public/js/shimapan.min.js', | |||
name: 'shimapan.bundle.js' | |||
}, | |||
{ | |||
src: 'public/js/panel.min.js', | |||
name: 'panel.bundle.js' | |||
} | |||
]; | |||
var tasks = files.map(function (entry) { | |||
return browserify({entries: [entry.src]}) | |||
.bundle() | |||
.pipe(source(entry.src)) | |||
.pipe(rename(entry.name)) | |||
.pipe(gulp.dest('public/js')); | |||
}); | |||
return evstream.merge.apply(null, tasks); | |||
}); |
@@ -2,41 +2,53 @@ | |||
"name": "shimapan", | |||
"version": "1.0.0", | |||
"dependencies": { | |||
"angular": "^1.6.6", | |||
"angular-messages": "^1.6.6", | |||
"angular-ui-router": "^0.4.3", | |||
"async": "^2.5.0", | |||
"body-parser": "^1.18.2", | |||
"config": "^1.26.2", | |||
"connect-mongo": "^2.0.0", | |||
"cookie-parser": "^1.4.3", | |||
"event-stream": "^3.3.4", | |||
"express": "^4.16.2", | |||
"express-jwt": "^5.3.0", | |||
"jsonwebtoken": "^8.1.0", | |||
"jwt-sign": "^0.1.0", | |||
"express-sanitizer": "^1.0.2", | |||
"express-session": "^1.15.6", | |||
"gulp-nodemon": "^2.2.1", | |||
"helmet": "^3.9.0", | |||
"method-override": "latest", | |||
"mongoose": "^4.12.1", | |||
"morgan": "^1.9.0", | |||
"multer": "^1.3.0", | |||
"ng-file-upload": "^12.2.13", | |||
"ngclipboard": "^1.1.2", | |||
"passport": "^0.4.0", | |||
"passport-local": "^1.0.0", | |||
"striptags": "^3.1.0" | |||
"passport-local-mongoose": "^4.2.1", | |||
"vinyl-source-stream": "^1.1.0" | |||
}, | |||
"description": "A simple file sharing website.", | |||
"main": "index.js", | |||
"devDependencies": { | |||
"browserify": "^14.5.0", | |||
"chai": "^4.1.2", | |||
"chai-http": "^3.0.0", | |||
"gulp": "latest", | |||
"gulp": "^3.9.1", | |||
"gulp-clean-css": "^3.9.0", | |||
"gulp-concat": "^2.6.1", | |||
"gulp-rename": "^1.2.2", | |||
"gulp-uglify": "^3.0.0", | |||
"mocha": "^4.0.1", | |||
"nyc": "^11.2.1" | |||
"nodemon": "^1.12.1", | |||
"npx": "^9.7.1" | |||
}, | |||
"author": "Jack Foltz", | |||
"license": "LICENSE", | |||
"scripts": { | |||
"test": "mocha" | |||
"test": "npx mocha", | |||
"build": "npx gulp", | |||
"start": "npx gulp start", | |||
"watch": "npx gulp watch" | |||
}, | |||
"repository": { | |||
"type": "git", | |||
@@ -1,100 +0,0 @@ | |||
*, *:before, *:after { | |||
margin: 0; | |||
padding: 0; | |||
box-sizing: border-box; | |||
} | |||
html, body { | |||
height: 100vh; | |||
} | |||
body { | |||
font: 600 14px 'Roboto', sans-serif; | |||
color: #000000; | |||
background: #000000; | |||
overflow-x: hidden; | |||
} | |||
.container { | |||
display: flex; | |||
min-height: 100%; | |||
} | |||
.sidebar { | |||
position: absolute; | |||
width: 220px; | |||
} | |||
.content { | |||
flex: 1; | |||
padding: 30px; | |||
background: #444444; | |||
box-shadow: 0 0 5px rgba(0,0,0,1); | |||
transform: translate3d(0,0,0); | |||
transition: transform .3s; | |||
color: #cccccc; | |||
} | |||
.content.isOpen { | |||
transform: translate3d(220px,0,0); | |||
} | |||
.button { | |||
cursor: pointer; | |||
} | |||
.button:before { | |||
content: '\f0c9'; | |||
font: 42px fontawesome; | |||
color: #eeeeee; | |||
} | |||
.sidebar-title { | |||
color: #2a9fd6; | |||
cursor: pointer; | |||
font-size: 16px; | |||
line-height: 50px; | |||
text-align: center; | |||
text-transform: uppercase; | |||
letter-spacing: 7px; | |||
border-bottom: 1px solid #222; | |||
background: #2a2a2a; | |||
} | |||
.nav li a { | |||
outline: 0; | |||
position: relative; | |||
display: block; | |||
padding: 20px 0 20px 50px; | |||
font-size: 14px; | |||
color: #eee; | |||
background: #333333; | |||
border-bottom: 1px solid #222; | |||
text-decoration: none; | |||
} | |||
.nav li a:before { | |||
font: 14px fontawesome; | |||
position: absolute; | |||
top: 19px; | |||
left: 20px; | |||
} | |||
.nav li:nth-child(1) a:before { content: '\f00a'; } | |||
.nav li:nth-child(2) a:before { content: '\f002'; } | |||
.nav li:nth-child(3) a:before { content: '\f084'; } | |||
.nav li:nth-child(4) a:before { content: '\f0e0'; } | |||
.nav li:nth-child(5) a:before { content: '\f0c0'; } | |||
.nav li:nth-child(6) a:before { content: '\f233'; } | |||
.nav li:nth-child(7) a:before { content: '\f023'; left: 23px; } | |||
.nav li a:hover { | |||
background: #444; | |||
} | |||
::-moz-focus-inner {border: 0;} | |||
.nav li a.active { | |||
box-shadow: inset 5px 0 0 #2A9FD6, inset 6px 0 0 #222; | |||
background: #444; | |||
} |
@@ -1,12 +0,0 @@ | |||
angular.module('NavCtrl', ['AuthSvc']).controller('NavController', ['$scope', '$window', 'AuthService', function($scope, $window, AuthService) { | |||
$scope.isLoggedIn = AuthService.isLoggedIn(); | |||
$scope.currentUser = AuthService.currentUser(); | |||
$scope.currentScope = AuthService.currentScope(); | |||
$scope.logout = AuthService.logout; | |||
$scope.hasPermission = function(permission) { | |||
if (!$scope.currentScope) return false; | |||
return $scope.currentScope.indexOf(permission) !== -1; | |||
}; | |||
}]); |
@@ -1,6 +0,0 @@ | |||
var app = angular.module('shimapan-panel', ['ui.router', 'NavCtrl', 'PanelRoutes']); | |||
app.run(['$rootScope', '$state', '$stateParams', function($rootScope, $state, $stateParams) { | |||
$rootScope.$state = $state; | |||
$rootScope.$stateParams = $stateParams; | |||
}]); |
@@ -1 +0,0 @@ | |||
var app = angular.module('shimapan', ['ngFileUpload', 'ngclipboard', 'UploadComp', 'RegisterComp', 'LoginComp']); |
@@ -5,16 +5,11 @@ | |||
<base href="/"/> | |||
<title>Shimapan</title> | |||
<link rel="stylesheet" href="/css/home.css"/> | |||
<link rel="stylesheet" href="/css/home.min.css"/> | |||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/> | |||
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/> | |||
<script src="/libs/angular/angular.min.js"></script> | |||
<script src="/libs/ng-file-upload/ng-file-upload-all.min.js"></script> | |||
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script> | |||
<script src="/libs/ngclipboard/src/ngclipboard.js"></script> | |||
<script src="/libs/app/shimapan.min.js"></script> | |||
<script src="/js/shimapan.bundle.js"></script> | |||
</head> | |||
<body ng-app="shimapan"> | |||
@@ -2,19 +2,29 @@ | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<link href="/css/index.css" type="text/css" rel="stylesheet"/> | |||
<script src="/js/typegraph.js" defer></script> | |||
<link href="/css/index.min.css" type="text/css" rel="stylesheet"/> | |||
<script src="/js/index.min.js" defer></script> | |||
<title>「shimapan.rocks」</title> | |||
</head> | |||
<body style="background:#000;margin:0;"> | |||
<body> | |||
<a href="/login"><img src="/img/flower.png"/></a> | |||
<canvas id="canvas"> | |||
きみのブラウザはキャンバスを対応しません。Chromeを ダウンロードしてください。 | |||
</canvas> | |||
<video autoplay loop id="video"> | |||
<source src="/img/edge.mp4" type="video/mp4"/> | |||
</video> | |||
<script>document.getElementById("video").volume = 0.2;</script> | |||
<script> | |||
var videos = [ | |||
"/img/edge.mp4", | |||
"/img/endthis.mp4", | |||
"/img/hell.mp4" | |||
]; | |||
var video = document.createElement("source"); | |||
video.setAttribute("src", videos[Math.floor(Math.random() * videos.length)]); | |||
video.setAttribute("type", "video/mp4"); | |||
document.getElementById("video").appendChild(video); | |||
document.getElementById("video").volume = 0.2; | |||
</script> | |||
<div id="buffer"></div> | |||
</body> | |||
</html> |
@@ -4,14 +4,9 @@ | |||
<meta charset="utf-8"/> | |||
<title>Login</title> | |||
<link href="/css/form.css" type="text/css" rel="stylesheet"/> | |||
<link href="/css/form.min.css" type="text/css" rel="stylesheet"/> | |||
<script src="/libs/angular/angular.min.js"></script> | |||
<script src="/libs/ng-file-upload/ng-file-upload-all.min.js"></script> | |||
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script> | |||
<script src="/libs/ngclipboard/src/ngclipboard.js"></script> | |||
<script src="/libs/app/shimapan.min.js"></script> | |||
<script src="/js/shimapan.bundle.js"></script> | |||
</head> | |||
<body ng-app="shimapan"> | |||
<div id="container"> | |||
@@ -4,22 +4,13 @@ | |||
<meta charset="utf-8"> | |||
<base href="/panel"/> | |||
<title>Panel</title> | |||
<link rel="stylesheet" href="/css/panel.css"/> | |||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/> | |||
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/> | |||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> | |||
<script src="/libs/angular/angular.min.js"></script> | |||
<script src="/libs/angular-ui-router/release/angular-ui-router.min.js"></script> | |||
<script src="/js/shimapan-panel/controllers/NavCtrl.js" defer></script> | |||
<script src="/libs/app/shimapan-panel.min.js"></script> | |||
<link rel="stylesheet" href="/css/panel.min.css"/> | |||
<script src="/js/panel.bundle.js"></script> | |||
</head> | |||
<body ng-app="shimapan-panel"> | |||
<div class="container"> | |||
<div class="sidebar" ng-controller="NavController"> | |||
<div class="sidebar-title"><a ui-sref="home">Shimapan</a></div> | |||
<div class="sidebar-title" ng-click="$state.go('home')"><span>Shimapan</span></div> | |||
<ul class="nav"> | |||
<li><a ui-sref="dashboard" ng-class="{active: $state.$current.name=='dashboard'}">Dashboard</a></li> | |||
<li><a ui-sref="search" ng-class="{active: $state.$current.name=='search'}">Search</a></li> | |||
@@ -27,11 +18,14 @@ | |||
<li><a ui-sref="invites" ng-class="{active: $state.$current.name=='invites'}">Invites</a></li> | |||
<li ng-hide="!hasPermission('users.view')"><a ui-sref="users" ng-class="{active: $state.$current.name=='users'}">Users</a></li> | |||
<li><a ui-sref="stats" ng-class="{active: $state.$current.name=='stats'}">Statistics</a></li> | |||
<li><a ui-sref="" ng-click="logout()">Logout</a></li> | |||
<li><a ng-click="logout()">Logout</a></li> | |||
</ul> | |||
</div> | |||
<div class="content isOpen" ng-class="{isOpen: open}"> | |||
<a class="button" ng-init="open = true" ng-click="open = !open"></a> | |||
<div class="content" ng-class="{isOpen: open}"> | |||
<div class="button" ng-class="{isOpen: open}" ng-init="open = false" ng-click="open = !open"> | |||
<div class="button-inner"></div> | |||
<span class="button-label">Menu</span> | |||
</div> | |||
<div class="view" ui-view></div> | |||
</div> | |||
</div> | |||
@@ -0,0 +1,30 @@ | |||
<div class="inner" ng-controller="ApiController" ng-init="currKey={};showModal=false;getKeys()"> | |||
<div class="keys"> | |||
<div class="key" ng-repeat="key in keys"> | |||
<i class="fa fa-key"></i> | |||
<span>{{key.identifier}}</span> | |||
</div> | |||
<div class="key add-key" ng-hide="keys.length >= 10"> | |||
<i class="fa fa-plus"></i> | |||
<span>Create</span> | |||
</div> | |||
</div> | |||
<div class="modal" ng-show="showModal==true"> | |||
<div class="modal-header"> | |||
<button class="close" type="button"></button> | |||
<h4 class="modal-title">API Key</h4> | |||
</div> | |||
<div class="modal-body modal-key"> | |||
<p>API Key:</p> | |||
<pre class="code">{{currKey.code}}</pre> | |||
<p>Use this key with your preferred method of utilizing the Shimapan API.</p> | |||
<p>Example with cURL:</p> | |||
<pre class="code"></pre> | |||
<div id="qr"></div> | |||
</div> | |||
<div class="modal-footer"> | |||
<button class="btn-delete" type="button"></button> | |||
<button class="btn-back" type="button"></button> | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1 @@ | |||
<h1>Dashboard</h1> |
@@ -4,14 +4,9 @@ | |||
<meta charset="utf-8"/> | |||
<title>Register</title> | |||
<link href="/css/form.css" type="text/css" rel="stylesheet"/> | |||
<link href="/css/form.min.css" type="text/css" rel="stylesheet"/> | |||
<script src="/libs/angular/angular.min.js"></script> | |||
<script src="/libs/ng-file-upload/ng-file-upload-all.min.js"></script> | |||
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script> | |||
<script src="/libs/ngclipboard/src/ngclipboard.js"></script> | |||
<script src="/libs/app/shimapan.min.js"></script> | |||
<script src="/js/shimapan.bundle.js"></script> | |||
</head> | |||
<body ng-app="shimapan"> | |||
<div id="container"> | |||
@@ -1 +0,0 @@ | |||
<h1>Panel api hurr durr</h1> |
@@ -1,2 +0,0 @@ | |||
<h1>This is yer home</h1> | |||
<p>This is a pargragraph</p> |
@@ -5,6 +5,9 @@ var mongoose = require('mongoose'); | |||
var morgan = require('morgan'); | |||
var passport = require('passport'); | |||
var cookieParser = require('cookie-parser'); | |||
var session = require('express-session'); | |||
var sanitizer = require('express-sanitizer'); | |||
var helmet = require('helmet'); | |||
var app = express(); | |||
@@ -19,20 +22,46 @@ var db = mongoose.connection; | |||
db.on('error', function(err) { | |||
if (err) console.log('MongoDB Connection Error: ', err); | |||
}); | |||
var MongoStore = require('connect-mongo')(session); | |||
var mongoStore = new MongoStore({ | |||
url: config.dbHost | |||
}); | |||
require('./config/passport.js'); | |||
app.use(helmet()); | |||
app.set('trust proxy', 1); | |||
app.use(session({ | |||
secret: 'secret', | |||
name: 'session.id', | |||
resave: false, | |||
saveUninitialized: false, | |||
store: mongoStore, | |||
cookie: { | |||
//secure: true, | |||
httpOnly: true, | |||
//domain: 'shimapan.rocks', | |||
maxAge: 1000 * 60 * 60 | |||
} | |||
})); | |||
app.use(passport.initialize()); | |||
app.use(passport.session()); | |||
app.use(cookieParser()); | |||
app.use(bodyParser.json()); | |||
app.use(bodyParser.json({ type: 'application/json' })); | |||
app.use(bodyParser.json({ type: 'application/*+json' })) | |||
app.use(bodyParser.urlencoded({ extended: true })); | |||
app.use(bodyParser.text()); | |||
app.use(sanitizer()); | |||
app.use(methodOverride('X-HTTP-Method-Override')); | |||
app.use(passport.initialize()); | |||
// Set /public to document root | |||
//app.use(favicon(__dirname + '/public/img/favicon.ico')); | |||
app.use(express.static(__dirname + '/public')); | |||
require('./app/routes')(app); | |||
require('./app/routes/routes.js')(app); | |||
require('./config/passport.js'); | |||
// Start app | |||
var port = process.env.PORT || 8080; | |||
@@ -6,7 +6,6 @@ var mongoose = require('mongoose'); | |||
var User = require('../app/models/User.js'); | |||
var Invite = require('../app/models/Invite.js'); | |||
var Upload = require('../app/models/Upload.js'); | |||
var Token = require('../app/models/Token.js'); | |||
var chai = require('chai'); | |||
var should = chai.should(); | |||
@@ -6,7 +6,6 @@ var mongoose = require('mongoose'); | |||
var User = require('../app/models/User.js'); | |||
var Invite = require('../app/models/Invite.js'); | |||
var Upload = require('../app/models/Upload.js'); | |||
var Token = require('../app/models/Token.js'); | |||
var chai = require('chai'); | |||
var http = require('chai-http'); | |||
@@ -24,7 +23,7 @@ chai.use(http); | |||
var resetDatabase = function(callback) { | |||
db.once('open', function() { | |||
async.each([ | |||
User, Invite, Upload, Token | |||
User, Invite, Upload, | |||
], function(schema, cb) { | |||
schema.remove({}, function(err) { | |||
cb(err); | |||
@@ -73,8 +72,6 @@ var register = function(user, cb) { | |||
var verifySuccessfulRegister = function(user, done) { | |||
register(user, function (err, res) { | |||
res.should.have.status(200); | |||
res.body.should.be.a('object'); | |||
res.body.should.have.property('token'); | |||
done(); | |||
}); | |||
}; | |||
@@ -111,8 +108,6 @@ var login = function(user, cb) { | |||
var verifySuccessfulLogin = function(user, done) { | |||
login(user, function (err, res) { | |||
res.should.have.status(200); | |||
res.body.should.be.a('object'); | |||
res.body.should.have.property('token'); | |||
done(); | |||
}); | |||
}; | |||