simple proxmox
This commit is contained in:
parent
f7cee0239e
commit
f18967ce8b
@ -3,16 +3,42 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const ejs = require('ejs')
|
const ejs = require('ejs')
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const {P2PSub} = require('p2psub');
|
||||||
|
const pvejs = require('./utils/pvejs');
|
||||||
|
|
||||||
|
|
||||||
// Set up the express app.
|
// Set up the express app.
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
app.onListen = [];
|
||||||
|
|
||||||
// Allow the express app to be exported into other files.
|
// Allow the express app to be exported into other files.
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
||||||
// Build the conf object from the conf files.
|
// Build the conf object from the conf files.
|
||||||
app.conf = require('./conf/conf');
|
app.conf = require('./conf/conf');
|
||||||
|
|
||||||
|
app.p2p = new P2PSub(app.conf.p2p);
|
||||||
|
|
||||||
|
app.onListen.push(function(){
|
||||||
|
app.p2p.subscribe(/./g, function(data, topic){
|
||||||
|
if(data.__noSocket) return;
|
||||||
|
|
||||||
|
app.io.emit('P2PSub', { topic, data })
|
||||||
|
});
|
||||||
|
|
||||||
|
app.io.on('connection', (socket) => {
|
||||||
|
// console.log('connection io', socket)
|
||||||
|
socket.on('P2PSub', (msg) => {
|
||||||
|
msg.data.__noSocket = true;
|
||||||
|
app.p2p.publish(msg.topic, msg.data);
|
||||||
|
socket.broadcast.emit('P2PSub', msg)
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
// Hold onto the auth middleware
|
// Hold onto the auth middleware
|
||||||
const middleware = require('./middleware/auth');
|
const middleware = require('./middleware/auth');
|
||||||
|
|
||||||
@ -39,8 +65,29 @@ app.use('/api/user', middleware.auth, require('./routes/user'));
|
|||||||
|
|
||||||
app.use('/api/token', middleware.auth, require('./routes/token'));
|
app.use('/api/token', middleware.auth, require('./routes/token'));
|
||||||
|
|
||||||
app.use('/api/group', middleware.auth, require('./routes/group'));
|
(async function(){
|
||||||
|
app.api = await pvejs(app.conf.proxmox);
|
||||||
|
})()
|
||||||
|
|
||||||
|
setInterval(async function(){
|
||||||
|
try{
|
||||||
|
let res = await app.api.GET({path: '/cluster/resources'});
|
||||||
|
let types = {};
|
||||||
|
|
||||||
|
for(let item of res.json){
|
||||||
|
if(!types[item.type]){
|
||||||
|
types[item.type] = [];
|
||||||
|
}
|
||||||
|
types[item.type].push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.p2p.publish('proxmox-cluster', {vpnSite: app.conf.vpnSite, data: types});
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
console.error('proxmox pub', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
// Catch 404 and forward to error handler. If none of the above routes are
|
// Catch 404 and forward to error handler. If none of the above routes are
|
||||||
// used, this is what will be called.
|
// used, this is what will be called.
|
||||||
|
@ -21,6 +21,10 @@ app.set('port', port);
|
|||||||
|
|
||||||
var server = http.createServer(app);
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
|
||||||
|
var io = require('socket.io')(server);
|
||||||
|
app.io = io;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen on provided port, on all network interfaces.
|
* Listen on provided port, on all network interfaces.
|
||||||
*/
|
*/
|
||||||
@ -87,4 +91,8 @@ function onListening() {
|
|||||||
? 'pipe ' + addr
|
? 'pipe ' + addr
|
||||||
: 'port ' + addr.port;
|
: 'port ' + addr.port;
|
||||||
debug('Listening on ' + bind);
|
debug('Listening on ' + bind);
|
||||||
|
|
||||||
|
for(let listener of app.onListen){
|
||||||
|
listener()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,27 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
userModel: 'ldap', // pam, redis, ldap
|
userModel: 'ldap', // pam, redis, ldap
|
||||||
ldap: {
|
ldap: {
|
||||||
url: 'ldap://192.168.1.54:389',
|
url: 'ldap://192.168.1.55:389',
|
||||||
bindDN: 'cn=admin,dc=theta42,dc=com',
|
bindDN: 'cn=ldapclient service,ou=people,dc=theta42,dc=com',
|
||||||
bindPassword: '__IN SRECREST FILE__',
|
bindPassword: '__IN SRECREST FILE__',
|
||||||
userBase: 'ou=people,dc=theta42,dc=com',
|
userBase: 'ou=people,dc=theta42,dc=com',
|
||||||
groupBase: 'ou=groups,dc=theta42,dc=com',
|
groupBase: 'ou=groups,dc=theta42,dc=com',
|
||||||
userFilter: '(objectClass=posixAccount)',
|
userFilter: '(objectClass=posixAccount)',
|
||||||
userNameAttribute: 'uid'
|
userNameAttribute: 'uid'
|
||||||
},
|
},
|
||||||
SENDGRID_API_KEy: '__IN SRECREST FILE__',
|
p2p: {
|
||||||
|
listenPort: 7575
|
||||||
|
},
|
||||||
|
proxmox: {
|
||||||
|
host: "__IN SRECREST FILE__",//this can be an ip or FQDN
|
||||||
|
authInfo: {
|
||||||
|
username: "__IN SRECREST FILE__",//this must include the username@realm
|
||||||
|
apiToken: "__IN SRECREST FILE__"//In the future, i would like this to be encrypted
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vpnSite: {
|
||||||
|
id: "__IN SRECREST FILE__",
|
||||||
|
name: "__IN SRECREST FILE__",
|
||||||
|
admin: "__IN SRECREST FILE__"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const sgMail = require('@sendgrid/mail');
|
|
||||||
const mustache = require('mustache');
|
|
||||||
const conf = require('../app').conf;
|
|
||||||
|
|
||||||
sgMail.setApiKey(conf.SENDGRID_API_KEY);
|
|
||||||
|
|
||||||
var Mail = {};
|
|
||||||
|
|
||||||
Mail.send = async function(to, subject, message, from){
|
|
||||||
await sgMail.send({
|
|
||||||
to: to,
|
|
||||||
from: from || 'Theta 42 Accounts <accounts@no-reply.theta42.com>',
|
|
||||||
subject: subject,
|
|
||||||
text: message,
|
|
||||||
html: message,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Mail.sendTemplate = async function(to, template, context, from){
|
|
||||||
template = require(`../views/email_templates/${template}`);
|
|
||||||
await Mail.send(
|
|
||||||
to,
|
|
||||||
mustache.render(template.subject, context),
|
|
||||||
mustache.render(template.message, context),
|
|
||||||
from || (template.from && mustache.render(template.message, context))
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {Mail};
|
|
@ -27,61 +27,6 @@ async function getGroups(client, member){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addGroup(client, data){
|
|
||||||
try{
|
|
||||||
|
|
||||||
await client.add(`cn=${data.name},${conf.groupBase}`, {
|
|
||||||
cn: data.name,
|
|
||||||
member: data.owner,
|
|
||||||
description: data.description,
|
|
||||||
owner: data.owner,
|
|
||||||
objectclass: [ 'groupOfNames', 'top' ]
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addMember(client, group, user){
|
|
||||||
try{
|
|
||||||
await client.modify(group.dn, [
|
|
||||||
new Change({
|
|
||||||
operation: 'add',
|
|
||||||
modification: new Attribute({
|
|
||||||
type: 'member',
|
|
||||||
values: [user.dn]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}catch(error){
|
|
||||||
// if(error = "TypeOrValueExistsError"){
|
|
||||||
// console.error('addMember error skipped', error)
|
|
||||||
// return ;
|
|
||||||
// }
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeMember(client, group, user){
|
|
||||||
try{
|
|
||||||
await client.modify(group.dn, [
|
|
||||||
new Change({
|
|
||||||
operation: 'delete',
|
|
||||||
modification: new Attribute({
|
|
||||||
type: 'member',
|
|
||||||
values: [user.dn]
|
|
||||||
})}),
|
|
||||||
]);
|
|
||||||
}catch(error){
|
|
||||||
if(error = "TypeOrValueExistsError")return ;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var Group = {};
|
var Group = {};
|
||||||
|
|
||||||
Group.list = async function(member){
|
Group.list = async function(member){
|
||||||
@ -151,63 +96,4 @@ Group.get = async function(data){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Group.add = async function(data){
|
|
||||||
try{
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await addGroup(client, data);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
return this.get(data);
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Group.addMember = async function(user){
|
|
||||||
try{
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await addMember(client, this, user);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Group.removeMember = async function(user){
|
|
||||||
try{
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await removeMember(client, this, user);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Group.remove = async function(){
|
|
||||||
try{
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await client.del(this.dn);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {Group};
|
module.exports = {Group};
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
const { Client, Attribute, Change } = require('ldapts');
|
const { Client, Attribute, Change } = require('ldapts');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
|
||||||
const {Mail} = require('./email');
|
|
||||||
const {Token, InviteToken, PasswordResetToken} = require('./token');
|
const {Token, InviteToken, PasswordResetToken} = require('./token');
|
||||||
const conf = require('../app').conf.ldap;
|
const conf = require('../app').conf.ldap;
|
||||||
|
|
||||||
@ -11,106 +10,6 @@ const client = new Client({
|
|||||||
url: conf.url,
|
url: conf.url,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function addPosixGroup(client, data){
|
|
||||||
|
|
||||||
try{
|
|
||||||
const groups = (await client.search(conf.groupBase, {
|
|
||||||
scope: 'sub',
|
|
||||||
filter: '(&(objectClass=posixGroup))',
|
|
||||||
})).searchEntries;
|
|
||||||
|
|
||||||
data.gidNumber = (Math.max(...groups.map(i => i.gidNumber))+1)+'';
|
|
||||||
|
|
||||||
await client.add(`cn=${data.cn},${conf.groupBase}`, {
|
|
||||||
cn: data.cn,
|
|
||||||
gidNumber: data.gidNumber,
|
|
||||||
objectclass: [ 'posixGroup', 'top' ]
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addPosixAccount(client, data){
|
|
||||||
try{
|
|
||||||
const people = (await client.search(conf.userBase, {
|
|
||||||
scope: 'sub',
|
|
||||||
filter: conf.userFilter,
|
|
||||||
})).searchEntries;
|
|
||||||
|
|
||||||
data.uidNumber = (Math.max(...people.map(i => i.uidNumber))+1)+'';
|
|
||||||
|
|
||||||
await client.add(`cn=${data.cn},${conf.userBase}`, {
|
|
||||||
cn: data.cn,
|
|
||||||
sn: data.sn,
|
|
||||||
uid: data.uid,
|
|
||||||
uidNumber: data.uidNumber,
|
|
||||||
gidNumber: data.gidNumber,
|
|
||||||
givenName: data.givenName,
|
|
||||||
mail: data.mail,
|
|
||||||
mobile: data.mobile,
|
|
||||||
loginShell: data.loginShell,
|
|
||||||
homeDirectory: data.homeDirectory,
|
|
||||||
userPassword: data.userPassword,
|
|
||||||
description: data.description || ' ',
|
|
||||||
sudoHost: 'ALL',
|
|
||||||
sudoCommand: 'ALL',
|
|
||||||
sudoUser: data.uid,
|
|
||||||
sshPublicKey: data.sshPublicKey,
|
|
||||||
objectclass: ['inetOrgPerson', 'sudoRole', 'ldapPublicKey', 'posixAccount', 'top' ]
|
|
||||||
});
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addLdapUser(client, data){
|
|
||||||
|
|
||||||
var group;
|
|
||||||
|
|
||||||
try{
|
|
||||||
data.uid = `${data.givenName[0]}${data.sn}`;
|
|
||||||
data.cn = data.uid;
|
|
||||||
data.loginShell = '/bin/bash';
|
|
||||||
data.homeDirectory= `/home/${data.uid}`;
|
|
||||||
data.userPassword = '{MD5}'+crypto.createHash('md5').update(data.userPassword, "binary").digest('base64');
|
|
||||||
|
|
||||||
group = await addPosixGroup(client, data);
|
|
||||||
data = await addPosixAccount(client, group);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
await deleteLdapDN(client, `cn=${data.uid},${conf.groupBase}`, true);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteLdapUser(client, data){
|
|
||||||
try{
|
|
||||||
await client.del(`cn=${data.cn},${conf.groupBase}`);
|
|
||||||
await client.del(data.dn);
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteLdapDN(client, dn, ignoreError){
|
|
||||||
try{
|
|
||||||
client.del(dn)
|
|
||||||
}catch(error){
|
|
||||||
if(!ignoreError) throw error;
|
|
||||||
console.error('ERROR: deleteLdapDN', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const user_parse = function(data){
|
const user_parse = function(data){
|
||||||
if(data[conf.userNameAttribute]){
|
if(data[conf.userNameAttribute]){
|
||||||
data.username = data[conf.userNameAttribute]
|
data.username = data[conf.userNameAttribute]
|
||||||
@ -225,195 +124,6 @@ User.exists = async function(data, key){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
User.add = async function(data) {
|
|
||||||
try{
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await addLdapUser(client, data);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
let user = await this.get(data.uid);
|
|
||||||
|
|
||||||
|
|
||||||
await Mail.sendTemplate(
|
|
||||||
user.mail,
|
|
||||||
'welcome',
|
|
||||||
{
|
|
||||||
user: user
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return user;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
if(error.message.includes('exists')){
|
|
||||||
let error = new Error('UserNameUsed');
|
|
||||||
error.name = 'UserNameUsed';
|
|
||||||
error.message = `LDAP:${data.uid} already exists`;
|
|
||||||
error.status = 409;
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
User.update = async function(data){
|
|
||||||
try{
|
|
||||||
let editableFeilds = ['mobile', 'sshPublicKey', 'description'];
|
|
||||||
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
for(let field of editableFeilds){
|
|
||||||
if(data[field]){
|
|
||||||
await client.modify(this.dn, [
|
|
||||||
new Change({
|
|
||||||
operation: 'replace',
|
|
||||||
modification: new Attribute({
|
|
||||||
type: field,
|
|
||||||
values: [data[field]]
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.unbind()
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
User.addByInvite = async function(data){
|
|
||||||
try{
|
|
||||||
let token = await InviteToken.get(data.token);
|
|
||||||
|
|
||||||
if(!token.is_valid && data.mailToken !== token.mail_token){
|
|
||||||
let error = new Error('Token Invalid');
|
|
||||||
error.name = 'Token Invalid';
|
|
||||||
error.message = `Token is not valid or as allready been used. ${data.token}`;
|
|
||||||
error.status = 401;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.mail = token.mail;
|
|
||||||
|
|
||||||
let user = await this.add(data);
|
|
||||||
|
|
||||||
if(user){
|
|
||||||
await token.consume({claimed_by: user.uid});
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
User.verifyEmail = async function(data){
|
|
||||||
try{
|
|
||||||
|
|
||||||
let exists = await this.exists(data.mail, 'mail');
|
|
||||||
|
|
||||||
if(exists) throw new Error('EmailInUse');
|
|
||||||
|
|
||||||
let token = await InviteToken.get(data.token);
|
|
||||||
await token.update({mail: data.mail})
|
|
||||||
await Mail.sendTemplate(
|
|
||||||
data.mail,
|
|
||||||
'validate_link',
|
|
||||||
{
|
|
||||||
link:`${data.url}/login/invite/${token.token}/${token.mail_token}`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
User.passwordReset = async function(url, mail){
|
|
||||||
try{
|
|
||||||
|
|
||||||
let user = await User.get({
|
|
||||||
searchKey: 'mail',
|
|
||||||
searchValue: mail
|
|
||||||
});
|
|
||||||
|
|
||||||
let token = await PasswordResetToken.add(user);
|
|
||||||
|
|
||||||
await Mail.sendTemplate(
|
|
||||||
user.mail,
|
|
||||||
'reset_link',
|
|
||||||
{
|
|
||||||
user: user,
|
|
||||||
link:`${url}/login/resetpassword/${token.token}`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}catch(error){
|
|
||||||
// if(error.name === 'UserNotFound') return false;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
User.remove = async function(data){
|
|
||||||
try{
|
|
||||||
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await deleteLdapUser(client, this);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
User.setPassword = async function(data){
|
|
||||||
try{
|
|
||||||
|
|
||||||
await client.bind(conf.bindDN, conf.bindPassword);
|
|
||||||
|
|
||||||
await client.modify(this.dn, [
|
|
||||||
new Change({
|
|
||||||
operation: 'replace',
|
|
||||||
modification: new Attribute({
|
|
||||||
type: 'userPassword',
|
|
||||||
values: ['{MD5}'+crypto.createHash('md5').update(data.userPassword, "binary").digest('base64')]
|
|
||||||
})}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await client.unbind();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
User.invite = async function(){
|
|
||||||
try{
|
|
||||||
let token = await InviteToken.add({created_by: this.uid});
|
|
||||||
|
|
||||||
return token;
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
User.login = async function(data){
|
User.login = async function(data){
|
||||||
try{
|
try{
|
||||||
let user = await this.get(data.uid);
|
let user = await this.get(data.uid);
|
||||||
|
1214
nodejs/package-lock.json
generated
1214
nodejs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,16 +12,16 @@
|
|||||||
"start": "node ./bin/www"
|
"start": "node ./bin/www"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sendgrid/mail": "^7.1.0",
|
|
||||||
"ejs": "^3.0.1",
|
"ejs": "^3.0.1",
|
||||||
"express": "~4.16.1",
|
"express": "~4.16.1",
|
||||||
"extend": "^3.0.2",
|
"extend": "^3.0.2",
|
||||||
"ldapts": "^2.2.1",
|
"ldapts": "^2.2.1",
|
||||||
"moment": "^2.25.3",
|
"moment": "^2.25.3",
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.0.1",
|
||||||
"nodemon": "^2.0.4",
|
"node-fetch": "^2.6.0",
|
||||||
|
"p2psub": "^0.1.6",
|
||||||
"redis": "^2.8.0",
|
"redis": "^2.8.0",
|
||||||
"smtpc": "^0.1.2"
|
"socket.io": "^2.3.0"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,5 +1,68 @@
|
|||||||
var app = {};
|
var app = {};
|
||||||
|
|
||||||
|
|
||||||
|
app.pubsub = (function(){
|
||||||
|
app.topics = {};
|
||||||
|
|
||||||
|
app.subscribe = function(topic, listener) {
|
||||||
|
if(topic instanceof RegExp){
|
||||||
|
listener.match = topic;
|
||||||
|
topic = "__REGEX__";
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the topic if not yet created
|
||||||
|
if(!app.topics[topic]) app.topics[topic] = [];
|
||||||
|
|
||||||
|
// add the listener
|
||||||
|
app.topics[topic].push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.matchTopics = function(topic){
|
||||||
|
topic = topic || '';
|
||||||
|
var matches = [... app.topics[topic] ? app.topics[topic] : []];
|
||||||
|
|
||||||
|
if(!app.topics['__REGEX__']) return matches;
|
||||||
|
|
||||||
|
for(var listener of app.topics['__REGEX__']){
|
||||||
|
if(topic.match(listener.match)) matches.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.publish = function(topic, data) {
|
||||||
|
|
||||||
|
// send the event to all listeners
|
||||||
|
app.matchTopics(topic).forEach(function(listener) {
|
||||||
|
setTimeout(function(data, topic){
|
||||||
|
listener(data || {}, topic);
|
||||||
|
}, 0, data, topic);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
})(app);
|
||||||
|
|
||||||
|
app.io = (function(app){
|
||||||
|
var socket = io();
|
||||||
|
// socket.emit('chat message', $('#m').val());
|
||||||
|
socket.on('P2PSub', function(msg){
|
||||||
|
msg.data.__noSocket = true;
|
||||||
|
app.publish(msg.topic, msg.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.subscribe(/./g, function(data, topic){
|
||||||
|
console.log('local_pubs', data, topic)
|
||||||
|
if(data.__noSocket) return;
|
||||||
|
// console.log('local_pubs 2', data, topic)
|
||||||
|
|
||||||
|
socket.emit('P2PSub', { topic, data })
|
||||||
|
});
|
||||||
|
|
||||||
|
return io;
|
||||||
|
|
||||||
|
})(app);
|
||||||
|
|
||||||
app.api = (function(app){
|
app.api = (function(app){
|
||||||
var baseURL = '/api/'
|
var baseURL = '/api/'
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const {User} = require('../models/user');
|
|
||||||
const {Auth, AuthToken} = require('../models/auth');
|
const {Auth, AuthToken} = require('../models/auth');
|
||||||
const {PasswordResetToken} = require('../models/token');
|
|
||||||
|
|
||||||
|
|
||||||
router.post('/login', async function(req, res, next){
|
router.post('/login', async function(req, res, next){
|
||||||
@ -31,86 +29,4 @@ router.all('/logout', async function(req, res, next){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/resetpassword', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let sent = await User.passwordReset(`${req.protocol}://${req.hostname}`, req.body.mail);
|
|
||||||
|
|
||||||
console.info('resetpassword for', req.body.mail, 'sent')
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
message: 'If the emaill address is in our system, you will receive a message.'
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/resetpassword/:token', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let token = await PasswordResetToken.get(req.params.token);
|
|
||||||
|
|
||||||
if(token.is_valid && 86400000+Number(token.created_on) > (new Date).getTime()){
|
|
||||||
let user = await User.get(token.created_by);
|
|
||||||
await user.setPassword(req.body);
|
|
||||||
token.update({is_valid: false});
|
|
||||||
return res.json({
|
|
||||||
message: 'Password has been changed.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/invite/:token/:mailToken', async function(req, res, next) {
|
|
||||||
try{
|
|
||||||
req.body.token = req.params.token;
|
|
||||||
req.body.mailToken = req.params.mailToken;
|
|
||||||
let user = await User.addByInvite(req.body);
|
|
||||||
let token = await AuthToken.add(user);
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
user: user.uid,
|
|
||||||
token: token.token
|
|
||||||
});
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/invite/:token', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let data = {
|
|
||||||
token: req.params.token,
|
|
||||||
url: `${req.protocol}://${req.hostname}`,
|
|
||||||
mail: req.body.mail,
|
|
||||||
}
|
|
||||||
|
|
||||||
await User.verifyEmail(data);
|
|
||||||
return res.send({message: 'sent'});
|
|
||||||
}catch(error){
|
|
||||||
next(error)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
/*
|
|
||||||
verify public ssh key
|
|
||||||
*/
|
|
||||||
// router.post('/verifykey', async function(req, res){
|
|
||||||
// let key = req.body.key;
|
|
||||||
|
|
||||||
// try{
|
|
||||||
// return res.json({
|
|
||||||
// info: await Users.verifyKey(key)
|
|
||||||
// });
|
|
||||||
// }catch(error){
|
|
||||||
// return res.status(400).json({
|
|
||||||
// message: 'Key is not a public key file!'
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// });
|
|
@ -1,82 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const router = require('express').Router();
|
|
||||||
const {User} = require('../models/user_ldap');
|
|
||||||
const {Group} = require('../models/group_ldap');
|
|
||||||
|
|
||||||
router.get('/', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let member = req.query.member ? await User.get(req.query.member) : {}
|
|
||||||
|
|
||||||
console.log('member', member)
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
results: await Group[req.query.detail ? "listDetail" : "list"](member.dn)
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
req.body.owner = req.user.dn;
|
|
||||||
return res.json({
|
|
||||||
results: await Group.add(req.body),
|
|
||||||
message: `${req.body.name} was added!`
|
|
||||||
})
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/:name', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
return res.json({
|
|
||||||
results: await Group.get(req.params.name)
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.put('/:name/:uid', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
var group = await Group.get(req.params.name);
|
|
||||||
var user = await User.get(req.params.uid);
|
|
||||||
return res.json({
|
|
||||||
results: group.addMember(user),
|
|
||||||
message: `Added user ${req.params.uid} to ${req.params.name} group.`
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.delete('/:name/:uid', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
var group = await Group.get(req.params.name);
|
|
||||||
var user = await User.get(req.params.uid);
|
|
||||||
return res.json({
|
|
||||||
results: group.removeMember(user),
|
|
||||||
message: `Removed user ${req.params.uid} from ${req.params.name} group.`
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.delete('/:name', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
var group = await Group.get(req.params.name);
|
|
||||||
return res.json({
|
|
||||||
removed: await group.remove(),
|
|
||||||
results: group,
|
|
||||||
message: `Group ${req.params.name} Deleted`
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
@ -12,64 +12,21 @@ router.get('/', async function(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* GET home page. */
|
/* GET home page. */
|
||||||
router.get('/users', function(req, res, next) {
|
|
||||||
res.render('users', { title: 'Express' });
|
router.get('/topics', function(req, res, next) {
|
||||||
|
res.render('topics', { title: 'Express' });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/users/:uid', function(req, res, next) {
|
router.get('/chat', function(req, res, next) {
|
||||||
res.render('home', { title: 'Express' });
|
res.render('chat', { title: 'Express' });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/groups', function(req, res, next) {
|
|
||||||
res.render('groups', { title: 'Express' });
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/login/resetpassword/:token', async function(req, res, next){
|
|
||||||
let token = await PasswordResetToken.get(req.params.token);
|
|
||||||
|
|
||||||
if(token.is_valid && 86400000+Number(token.created_on) > (new Date).getTime()){
|
|
||||||
res.render('reset_password', {token:token});
|
|
||||||
}else{
|
|
||||||
next({message: 'token not found', status: 404});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/login/invite/:token/:mailToken', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
|
|
||||||
let token = await InviteToken.get(req.params.token);
|
|
||||||
|
|
||||||
if(token.is_valid && token.mail !== '__NONE__' && token.mail_token === req.params.mailToken){
|
|
||||||
token.created_on = moment(token.created_on, 'x').fromNow();
|
|
||||||
res.render('invite', { title: 'Express', invite: token });
|
|
||||||
}else{
|
|
||||||
next({message: 'token not found', status: 404});
|
|
||||||
}
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/login/invite/:token', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let token = await InviteToken.get(req.params.token);
|
|
||||||
token.created_on = moment(token.created_on, 'x').fromNow();
|
|
||||||
|
|
||||||
if(token.is_valid){
|
|
||||||
res.render('invite_email', { title: 'Express', invite: token });
|
|
||||||
}else{
|
|
||||||
next({message: 'token not found', status: 404});
|
|
||||||
}
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/* GET home page. */
|
|
||||||
router.get('/login', function(req, res, next) {
|
router.get('/login', function(req, res, next) {
|
||||||
res.render('login', {redirect: req.query.redirect});
|
res.render('login', {redirect: req.query.redirect});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/proxmox', function(req, res, next) {
|
||||||
|
res.render('proxmox', {});
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
83
nodejs/routes/user.js
Executable file → Normal file
83
nodejs/routes/user.js
Executable file → Normal file
@ -13,42 +13,6 @@ router.get('/', async function(req, res, next){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
req.body.created_by = req.user.uid
|
|
||||||
|
|
||||||
return res.json({results: await User.add(req.body)});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.delete('/:uid', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let user = await User.get(req.params.uid);
|
|
||||||
|
|
||||||
return res.json({uid: req.params.uid, results: await user.remove()})
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.put('/:uid', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let user = await User.get(req.params.uid);
|
|
||||||
|
|
||||||
// console.log('update user', user);
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
results: await user.update(req.body),
|
|
||||||
message: `Updated ${req.params.uid} user`
|
|
||||||
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/me', async function(req, res, next){
|
router.get('/me', async function(req, res, next){
|
||||||
try{
|
try{
|
||||||
|
|
||||||
@ -58,53 +22,6 @@ router.get('/me', async function(req, res, next){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/password', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
return res.json({results: await req.user.setPassword(req.body)})
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.put('/:uid/password', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let user = await User.get(req.params.uid);
|
|
||||||
return res.json({
|
|
||||||
results: await user.setPassword(req.body),
|
|
||||||
message: `User ${user.uid} password changed.`
|
|
||||||
});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/invite', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let token = await req.user.invite();
|
|
||||||
|
|
||||||
return res.json({token: token.token});
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/key', async function(req, res, next){
|
|
||||||
try{
|
|
||||||
let added = await User.addSSHkey({
|
|
||||||
uid: req.user.uid,
|
|
||||||
key: req.body.key
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(added === true ? 200 : 400).json({
|
|
||||||
message: added
|
|
||||||
});
|
|
||||||
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/:uid', async function(req, res, next){
|
router.get('/:uid', async function(req, res, next){
|
||||||
try{
|
try{
|
||||||
return res.json({
|
return res.json({
|
||||||
|
214
nodejs/utils/pvejs.js
Normal file
214
nodejs/utils/pvejs.js
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
const { URLSearchParams } = require('url');
|
||||||
|
|
||||||
|
var apiProto = {};
|
||||||
|
|
||||||
|
apiProto.__getApiDoc = async function(host){
|
||||||
|
try{
|
||||||
|
let setupRes = await fetch(host || this.host+'/pve-docs/api-viewer/apidoc.js', { method: 'GET'});
|
||||||
|
let text = await setupRes.text()
|
||||||
|
|
||||||
|
text = text.split('// avoid errors when running without development tools')[0];
|
||||||
|
|
||||||
|
return eval(text += '; pveapi;');
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apiProto.__makeMap = async function(APItree){
|
||||||
|
APItree = APItree || await this.__getApiDoc();
|
||||||
|
let apiMap = {};
|
||||||
|
|
||||||
|
function __rec(obj){
|
||||||
|
for(let item of obj){
|
||||||
|
if(item.children){
|
||||||
|
__rec(item.children);
|
||||||
|
}
|
||||||
|
if(item.path){
|
||||||
|
delete item.children;
|
||||||
|
apiMap[item.path] = item;
|
||||||
|
item.methods = item.info;
|
||||||
|
delete item.info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__rec(APItree);
|
||||||
|
|
||||||
|
return apiMap
|
||||||
|
};
|
||||||
|
|
||||||
|
//these helper functions are to build the fetch call
|
||||||
|
apiProto.__buildHeaders = function(){
|
||||||
|
let headers = {};
|
||||||
|
if(this.authInfo.apiToken){
|
||||||
|
headers = {
|
||||||
|
"Authorization": `PVEAPIToken=${this.authInfo.username}=${this.authInfo.apiToken}`
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
headers = {
|
||||||
|
'Cookie': 'PVEAuthCookie='+this.user.ticket,
|
||||||
|
'CSRFPreventionToken': this.user.CSRFPreventionToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this function takes an iterable of 'key':'value' pairs
|
||||||
|
apiProto.__buildBody = function(props, data){
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
for (let key in props){
|
||||||
|
if(data[key]){
|
||||||
|
params.append(key, data[key])
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(props[key].optional !==1){
|
||||||
|
// console.log(props[key])
|
||||||
|
throw new Error('MissingKey');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.__getEndPoint = function(args){
|
||||||
|
let endpoint = this.apiMap[args.path]
|
||||||
|
if(!args || !endpoint) throw new Error('endpointNotFound');
|
||||||
|
if(!endpoint.methods[args.method]) throw new Error('methodNotFound');
|
||||||
|
|
||||||
|
return endpoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.__fetch = async function(method, path, params, data){
|
||||||
|
try{
|
||||||
|
let endpoint = this.__getEndPoint({path, method});
|
||||||
|
|
||||||
|
let fetchOptions = {
|
||||||
|
method: method,
|
||||||
|
headers: this.__buildHeaders(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if(['PUT', 'POST'].includes(method)){
|
||||||
|
fetchOptions.body = this.__buildBody(
|
||||||
|
endpoint.methods[method].parameters.properties,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let HTTPres = await fetch(this.BASEURL+path, fetchOptions);
|
||||||
|
|
||||||
|
// console.log(HTTPres)
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: HTTPres.status,
|
||||||
|
statusText: HTTPres.statusText,
|
||||||
|
json: (await HTTPres.json()).data,
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.GET = async function(args){
|
||||||
|
try{
|
||||||
|
|
||||||
|
return await this.__fetch(
|
||||||
|
'GET',
|
||||||
|
args.path,
|
||||||
|
args.parama || {},
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.DELETE = async function(args){
|
||||||
|
try{
|
||||||
|
|
||||||
|
return await this.__fetch(
|
||||||
|
'DELETE',
|
||||||
|
args.path,
|
||||||
|
args.parama || {},
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.POST = async function(args){
|
||||||
|
try{
|
||||||
|
|
||||||
|
return await this.__fetch(
|
||||||
|
'POST',
|
||||||
|
args.path,
|
||||||
|
args.parama || {},
|
||||||
|
args.data || {}
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.PUT = async function(args){
|
||||||
|
try{
|
||||||
|
|
||||||
|
return await this.__fetch(
|
||||||
|
'PUT',
|
||||||
|
args.path,
|
||||||
|
args.parama || {},
|
||||||
|
args.data || {}
|
||||||
|
);
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.auth = async function(authInfo){
|
||||||
|
try{
|
||||||
|
this.user = (await this.POST({
|
||||||
|
path: '/access/ticket',
|
||||||
|
data: authInfo || this.authInfo
|
||||||
|
})).json
|
||||||
|
|
||||||
|
return authInfo;
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
apiProto.__init = async function(conf){
|
||||||
|
try{
|
||||||
|
this.user = {};
|
||||||
|
this.BASEURL = conf.host+'/api2/json';
|
||||||
|
this.apiMap = await this.__makeMap();
|
||||||
|
|
||||||
|
if(!this.authInfo.apiToken) await this.auth();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
throw(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function API(conf){
|
||||||
|
let instance = Object.create(apiProto);
|
||||||
|
Object.assign(instance, conf);
|
||||||
|
|
||||||
|
return await instance.__init(conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = API;
|
||||||
|
|
79
nodejs/views/chat.ejs
Normal file
79
nodejs/views/chat.ejs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<%- include('top') %>
|
||||||
|
<script id="rowTemplate" type="text/html">
|
||||||
|
<p>
|
||||||
|
<b>{{ uid }}:</b> {{message}}
|
||||||
|
</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var publishTopic = function(btn){
|
||||||
|
event.preventDefault(); // avoid to execute the actual submit of the form.
|
||||||
|
var $form = $(btn).closest( '[action]' ); // gets the 'form' parent
|
||||||
|
var formData = $form.find( '[name]' ).serializeObject();
|
||||||
|
|
||||||
|
if( !$form.validate()) {
|
||||||
|
app.util.actionMessage('Please fix the form errors.', $form, 'danger')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('formData', formData)
|
||||||
|
|
||||||
|
app.publish('p2p-chat', {
|
||||||
|
uid: app.auth.user.uid,
|
||||||
|
message: formData.message
|
||||||
|
})
|
||||||
|
$form.trigger("reset");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
app.subscribe("p2p-chat", function(data, topic){
|
||||||
|
var rowTemplate = $('#rowTemplate').html();
|
||||||
|
var $target = $('#tableAJAX');
|
||||||
|
|
||||||
|
user_row = Mustache.render(rowTemplate, data);
|
||||||
|
$target.append(user_row);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<div class="row" style="display:none">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="fas fa-layer-plus"></i>
|
||||||
|
Chat
|
||||||
|
</div>
|
||||||
|
<div class="card-header actionMessage" style="display:none"></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action='__internal__' onsubmit="publishTopic(this)">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">message</label>
|
||||||
|
<textarea class="form-control shadow" name="message" placeholder="{...}" validate=":1"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-outline-dark">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="fad fa-users-class"></i>
|
||||||
|
Incoming Chats
|
||||||
|
</div>
|
||||||
|
<div class="card-header actionMessage" style="display:none">
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="" id="tableAJAX">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%- include('bottom') %>
|
@ -1,25 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
subject: 'Password reset for Theta 42 account',
|
|
||||||
message: `
|
|
||||||
<h2> Theta 42 account</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Hello {{ user.givenName }},
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
You have asked to reset the password for user name <b>{{ user.uid }}</b> . Please
|
|
||||||
click the link below to complete this request. If this was done in errror,
|
|
||||||
please ignore this email.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{ link }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</p>
|
|
||||||
Thank you,<br />
|
|
||||||
Theta 42
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
};
|
|
@ -1,24 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
subject: 'Validate email for Theta 42 account',
|
|
||||||
message: `
|
|
||||||
<h2> Theta 42 account</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Welcome,
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
We need to verify the provided email address in order to continue. Please
|
|
||||||
follow the link below to verify this email address:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{ link }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</p>
|
|
||||||
Thank you,<br />
|
|
||||||
Theta 42
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
};
|
|
@ -1,34 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
subject: 'Welcome to Theta 42!',
|
|
||||||
message: `
|
|
||||||
<p>
|
|
||||||
Welcome {{user.givenName}},
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Your new Theta 42 Single sign-on account is ready to use. Here is some
|
|
||||||
information to get you started.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Your username is <b>{{user.uid}}</b>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
You can manage your account at https://sso.theta42.com
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
You account is ready to be used now, test it by SSHing into the Theta 42
|
|
||||||
jump host \`ssh {{user.uid}}@718it.biz\`
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The SSO service is still in beta, so please report any bugs you may find!
|
|
||||||
You will be notified of new features and services as they become available.
|
|
||||||
</p>
|
|
||||||
Thank you,<br />
|
|
||||||
Theta 42
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
};
|
|
@ -1,134 +0,0 @@
|
|||||||
<%- include('top') %>
|
|
||||||
<script id="rowTemplate" type="text/html">
|
|
||||||
<p>
|
|
||||||
<div class="card shadow">
|
|
||||||
<div class="card-header">
|
|
||||||
<i class="fad fa-users-class"></i>
|
|
||||||
Group: {{ cn }}
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p>
|
|
||||||
{{ description }}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<ul class="list-group">
|
|
||||||
{{ #member }}
|
|
||||||
<li class="list-group-item shadow">
|
|
||||||
<i class="fad fa-user"></i> {{ uid }}
|
|
||||||
<button type="button" action="group/{{groupCN}}/{{uid}}" method="delete" onclick="formAJAX(this)" evalAJAX="tableAJAX(data.message)" class="btn btn-sm btn-danger float-right">
|
|
||||||
<i class="fad fa-user-slash"></i>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
{{ /member }}
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<i class="fad fa-user-plus"></i>
|
|
||||||
</button>
|
|
||||||
<div class="dropdown-menu shadow-lg" aria-labelledby="dropdownMenuButton">
|
|
||||||
{{ #toAdd }}{{#.}}
|
|
||||||
<a class="dropdown-item" action="group/{{groupCN}}/{{uid}}" method="put" onclick="formAJAX(this)" evalAJAX="tableAJAX(data.message)">
|
|
||||||
<i class="fad fa-user"></i> {{uid}}
|
|
||||||
</a>
|
|
||||||
{{/.}}{{ /toAdd }}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" onclick="app.group.remove({cn: '{{cn}}'}, function(){tableAJAX('Group {{cn}} deleted.')})" class="btn btn-danger float-right">
|
|
||||||
<i class="fad fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
var userlist;
|
|
||||||
|
|
||||||
function getUserList(callback){
|
|
||||||
app.user.list(function(error, data){
|
|
||||||
userlist = data.results;
|
|
||||||
callback()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function tableAJAX(actionMessage){
|
|
||||||
var rowTemplate = $('#rowTemplate').html();
|
|
||||||
var $target = $('#tableAJAX');
|
|
||||||
|
|
||||||
$target.html('').hide();
|
|
||||||
app.util.actionMessage('Refreshing user list...', $target);
|
|
||||||
|
|
||||||
app.group.list(function(error, data){
|
|
||||||
$.each( data.results, function(key, value) {
|
|
||||||
|
|
||||||
// console.log(value.member)
|
|
||||||
|
|
||||||
value.toAdd = userlist.map(function(user){
|
|
||||||
if(!value.member.includes(user.dn)) return user;
|
|
||||||
})
|
|
||||||
value.member = value.member.map(function(user){
|
|
||||||
return {
|
|
||||||
dn: user,
|
|
||||||
uid: user.match(/cn=[a-zA-Z0-9\_\-\@\.]+/)[0].replace('cn=', '')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
value.groupCN = value.cn;
|
|
||||||
user_row = Mustache.render(rowTemplate, value);
|
|
||||||
$target.append(user_row);
|
|
||||||
});
|
|
||||||
|
|
||||||
$target.fadeIn('slow');
|
|
||||||
|
|
||||||
app.util.actionMessage(actionMessage || '', $target, 'info');
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
|
||||||
getUserList(tableAJAX);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<div class="row" style="display:none">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card shadow-lg">
|
|
||||||
<div class="card-header">
|
|
||||||
<i class="fas fa-layer-plus"></i>
|
|
||||||
Add new group
|
|
||||||
</div>
|
|
||||||
<div class="card-header actionMessage" style="display:none"></div>
|
|
||||||
<div class="card-body">
|
|
||||||
<form action="group/" method="post" onsubmit="formAJAX(this)" evalAJAX="tableAJAX('')">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">Name</label>
|
|
||||||
<input type="text" class="form-control shadow" name="name" placeholder="app_gitea_admin" validate=":3" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">Description</label>
|
|
||||||
<textarea class="form-control shadow" name="description" placeholder="Admin group for gitea app" validate=":3"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-outline-dark">Add</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card shadow-lg">
|
|
||||||
<div class="card-header">
|
|
||||||
<i class="fad fa-users-class"></i>
|
|
||||||
Group list
|
|
||||||
</div>
|
|
||||||
<div class="card-header actionMessage" style="display:none">
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="" id="tableAJAX">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<%- include('bottom') %>
|
|
@ -1,39 +0,0 @@
|
|||||||
<%- include('top') %>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function tableAJAX(message){
|
|
||||||
app.util.actionMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
|
||||||
$('form').attr('action', 'auth/invite/<%= invite.token %>/<%= invite.mail_token %>').attr('evalAJAX', 'location.replace("/login");')
|
|
||||||
$('[name="mail"').val('<%= invite.mail %>').prop("disabled", true);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
div.form-group:hover {
|
|
||||||
-ms-transform: scale(1.02);
|
|
||||||
-webkit-transform: scale(1.02);
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="row" style="display:none">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="card shadow-lg">
|
|
||||||
<div class="card-header">
|
|
||||||
Add new user
|
|
||||||
</div>
|
|
||||||
<div class="card-header actionMessage" style="display:none">
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p>
|
|
||||||
Invited By: <b><%= invite.created_by %></b>, <%= invite.created_on %>
|
|
||||||
</p>
|
|
||||||
<%- include('user_form') %>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<%- include('bottom') %>
|
|
@ -1,59 +0,0 @@
|
|||||||
<%- include('top') %>
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
|
|
||||||
var emailSent = function(){
|
|
||||||
$('#email_card .card-body').html("<h1>Thank you!</h1><p>Check your mail</p>")
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
div.form-group:hover {
|
|
||||||
-ms-transform: scale(1.02);
|
|
||||||
-webkit-transform: scale(1.02);
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div id="email_card" class="card-deck">
|
|
||||||
<div class="shadow-lg card mb-3">
|
|
||||||
<div class="card-header">
|
|
||||||
Validate Email
|
|
||||||
</div>
|
|
||||||
<div class="card-header actionMessage" style="display:none">
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p>
|
|
||||||
Invited By: <b><%= invite.created_by %></b>, <%= invite.created_on %>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Please enter a valid email address. A link will be sent to
|
|
||||||
the supplied address to complete the registration process.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The supplied email will also be used as the linked email for
|
|
||||||
the new user.
|
|
||||||
</p>
|
|
||||||
<form action="auth/invite/<%= invite.token %>" onsubmit="formAJAX(this)" evalAJAX="emailSent()">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">Email</label>
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<span class="input-group-text" ><i class="fad fa-at"></i></span>
|
|
||||||
</div>
|
|
||||||
<input type="email" name="mail" class="form-control" placeholder="jsmith@gmail.com" validate="email:3" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-outline-dark"><i class="fad fa-paper-plane"></i> Send It!</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<%- include('bottom') %>
|
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
app.auth.isLoggedIn(function(error, isLoggedIn){
|
app.auth.isLoggedIn(function(error, isLoggedIn){
|
||||||
if(isLoggedIn){
|
if(isLoggedIn){
|
||||||
window.location.href = app.util.getUrlParameter('redirect') || '/';
|
window.location.href = app.util.getUrlParameter('redirect') || '/topics';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -48,53 +48,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="shadow-lg card border-danger mb-3">
|
|
||||||
<div class="card-header shadow">
|
|
||||||
Social Login
|
|
||||||
</div>
|
|
||||||
<div class="card-header shadow actionMessage" style="display:none">
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h3>Coming soon!</h3>
|
|
||||||
<p>
|
|
||||||
<ul class="list-group">
|
|
||||||
<li class="list-group-item"><i class="fab fa-google"></i> Login with google OATH</li>
|
|
||||||
<li class="list-group-item"><i class="fab fa-github"></i> Login with github OATH</li>
|
|
||||||
<li class="list-group-item"><i class="fab fa-facebook"></i> Login with facebook OATH</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shadow-lg card mb-3">
|
|
||||||
<div class="card-header shadow">
|
|
||||||
Password Reset
|
|
||||||
</div>
|
|
||||||
<div class="card-header shadow actionMessage" style="display:none">
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p>
|
|
||||||
Forgot your password? Or your user name? No problem! Just
|
|
||||||
enter you email address below and if you are in our system,
|
|
||||||
we will email with the required information to get back up
|
|
||||||
and running!
|
|
||||||
</p>
|
|
||||||
<form action="auth/resetpassword" onsubmit="formAJAX(this)">
|
|
||||||
<input type="hidden" name="redirect" value="<%= redirect %>">
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">Email</label>
|
|
||||||
<div class="input-group mb-3 shadow">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<span class="input-group-text" ><i class="fad fa-at"></i></span>
|
|
||||||
</div>
|
|
||||||
<input type="email" name="mail" class="form-control" placeholder="jsmith@gmail.com" validate="email:3" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-outline-dark"><i class="fad fa-question"></i> Help me!</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%- include('bottom') %>
|
<%- include('bottom') %>
|
||||||
|
114
nodejs/views/proxmox.ejs
Normal file
114
nodejs/views/proxmox.ejs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<%- include('top') %>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var proxmoxSites = {};
|
||||||
|
|
||||||
|
function formatBytes(a,b=2){if(0===a)return"0 Bytes";const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}
|
||||||
|
|
||||||
|
|
||||||
|
var parseNodeData = function(data){
|
||||||
|
cluster = data.data
|
||||||
|
cluster.onlineCount = 0;
|
||||||
|
cluster.cpu = 0
|
||||||
|
cluster.maxcpu = 0
|
||||||
|
cluster.maxmem = 0
|
||||||
|
cluster.mem = 0
|
||||||
|
|
||||||
|
|
||||||
|
for(let node of data.data.node){
|
||||||
|
// console.log('parse', node)
|
||||||
|
if(node.status === "offline") continue;
|
||||||
|
cluster.onlineCount++
|
||||||
|
cluster.cpu += node.cpu
|
||||||
|
cluster.maxcpu += node.maxcpu
|
||||||
|
cluster.maxmem += node.maxmem
|
||||||
|
cluster.mem += node.mem
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.cpu = parseFloat(((cluster.cpu/cluster.onlineCount)*100).toFixed(2));
|
||||||
|
cluster.maxmem = formatBytes(cluster.maxmem)
|
||||||
|
cluster.mem = formatBytes(cluster.mem)
|
||||||
|
|
||||||
|
|
||||||
|
$.extend(data.data, cluster);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
app.subscribe('proxmox-cluster', function(data){
|
||||||
|
if(!proxmoxSites[data.vpnSite.name]){
|
||||||
|
let index = $.scope.proxmox.push(parseNodeData(data));
|
||||||
|
proxmoxSites[data.vpnSite.name] = {
|
||||||
|
data:data,
|
||||||
|
index: index
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
|
||||||
|
$.scope.proxmox.update(proxmoxSites[data.vpnSite.name].index, parseNodeData(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="shadow-lg card mb-3" jq-repeat="proxmox">
|
||||||
|
<div class="card-header">
|
||||||
|
{{ vpnSite.name }}
|
||||||
|
</div>
|
||||||
|
<div class="card-header actionMessage" style="display:none">
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
Nodes online {{data.onlineCount}} of {{data.node.length}} <br />
|
||||||
|
CPU: {{data.cpu}}% of {{data.maxcpu}} cores <br />
|
||||||
|
Ram: {{data.mem}} of {{data.maxmem}}
|
||||||
|
<ul>
|
||||||
|
{{#data.lxc}}
|
||||||
|
<li>{{name}} -- {{status}}</li>
|
||||||
|
{{/data.lxc}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <div id="email_card" class="card-deck">
|
||||||
|
<div class="shadow-lg card mb-3">
|
||||||
|
<div class="card-header">
|
||||||
|
Validate Email
|
||||||
|
</div>
|
||||||
|
<div class="card-header actionMessage" style="display:none">
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>
|
||||||
|
Invited By: <b></b>,.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please enter a valid email address. A link will be sent to
|
||||||
|
the supplied address to complete the registration process.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The supplied email will also be used as the linked email for
|
||||||
|
the new user.
|
||||||
|
</p>
|
||||||
|
<form action="auth/invite/" onsubmit="formAJAX(this)" evalAJAX="emailSent()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">Email</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text" ><i class="fad fa-at"></i></span>
|
||||||
|
</div>
|
||||||
|
<input type="email" name="mail" class="form-control" placeholder="jsmith@gmail.com" validate="email:3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-outline-dark"><i class="fad fa-paper-plane"></i> Send It!</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
<%- include('bottom') %>
|
@ -3,19 +3,22 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>SSO Manager - Theta 42</title>
|
<title>VPN-p2p - Theta 42</title>
|
||||||
<!-- CSS are placed here -->
|
<!-- CSS are placed here -->
|
||||||
<!-- <link rel='stylesheet' href='/static/css/bootstrap.min.css' /> -->
|
<!-- <link rel='stylesheet' href='/static/css/bootstrap.min.css' /> -->
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||||
<link rel='stylesheet' href='/static/css/styles.css' />
|
<link rel='stylesheet' href='/static/css/styles.css' />
|
||||||
<!-- Scripts are placed here -->
|
<!-- Scripts are placed here -->
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.5.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.5.0.min.js"></script>
|
||||||
|
|
||||||
<!-- <script type="text/javascript" src='/static/js/jquery.min.js'></script> -->
|
<!-- <script type="text/javascript" src='/static/js/jquery.min.js'></script> -->
|
||||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
||||||
<!-- <script type="text/javascript" src='/static/js/bootstrap.min.js'></script> -->
|
<!-- <script type="text/javascript" src='/static/js/bootstrap.min.js'></script> -->
|
||||||
<script src="https://kit.fontawesome.com/4625ee80a2.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/4625ee80a2.js" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" src='/static/js/mustache.min.js'></script>
|
<script type="text/javascript" src='/static/js/mustache.min.js'></script>
|
||||||
|
<script src="//stuff.718it.biz/jq-repeat.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/app.js"></script>
|
<script type="text/javascript" src="/static/js/app.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/val.js"></script>
|
<script type="text/javascript" src="/static/js/val.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/moment.js"></script>
|
<script type="text/javascript" src="/static/js/moment.js"></script>
|
||||||
@ -28,11 +31,10 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="shadow d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
|
<header class="shadow d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
|
||||||
<h5 class="hover-effect my-0 mr-md-auto font-weight-normal">SSO Manager - Theta 42</h5>
|
<h5 class="hover-effect my-0 mr-md-auto font-weight-normal">VPN p2p - Theta 42</h5>
|
||||||
<nav class="my-2 my-md-0 mr-md-3">
|
<nav class="my-2 my-md-0 mr-md-3">
|
||||||
<a class="text-dark hover-effect" href="/"><i class="fad fa-tachometer-alt-fastest"></i> Home</a>
|
<a class="text-dark hover-effect" href="/topics"><i class="fad fa-tachometer-alt-fastest"></i> Topics</a>
|
||||||
<a class="text-dark hover-effect" href="/users"><i class="fad fa-users"></i> Users</a>
|
<a class="text-dark hover-effect" href="/chat"><i class="fad fa-tachometer-alt-fastest"></i> Chat</a>
|
||||||
<a class="text-dark hover-effect" href="/groups"><i class="fad fa-users-class"></i> Groups</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
<a class="hover-effect btn btn-outline-primary" onclick="app.auth.logOut(e => window.location.href='/')"><i class="fas fa-sign-out"></i> Log Out</a>
|
<a class="hover-effect btn btn-outline-primary" onclick="app.auth.logOut(e => window.location.href='/')"><i class="fas fa-sign-out"></i> Log Out</a>
|
||||||
|
|
||||||
|
88
nodejs/views/topics.ejs
Normal file
88
nodejs/views/topics.ejs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<%- include('top') %>
|
||||||
|
<script id="rowTemplate" type="text/html">
|
||||||
|
<p>
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="fad fa-users-class"></i>
|
||||||
|
<b>Topic:</b> {{ topic }}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{{ data }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var publishTopic = function(btn){
|
||||||
|
event.preventDefault(); // avoid to execute the actual submit of the form.
|
||||||
|
var $form = $(btn).closest( '[action]' ); // gets the 'form' parent
|
||||||
|
var formData = $form.find( '[name]' ).serializeObject();
|
||||||
|
|
||||||
|
if( !$form.validate()) {
|
||||||
|
app.util.actionMessage('Please fix the form errors.', $form, 'danger')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.publish(formData.topic, JSON.parse(formData.data))
|
||||||
|
$form.trigger("reset");
|
||||||
|
app.util.actionMessage('Topic '+formData.topic+' published!', $form, 'success'); //re-populate table
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
app.subscribe(/./g, function(data, topic){
|
||||||
|
var rowTemplate = $('#rowTemplate').html();
|
||||||
|
var $target = $('#tableAJAX');
|
||||||
|
|
||||||
|
user_row = Mustache.render(rowTemplate, {topic, data: JSON.stringify(data)});
|
||||||
|
$target.append(user_row);
|
||||||
|
$target.fadeIn('slow');
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<div class="row" style="display:none">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="fas fa-layer-plus"></i>
|
||||||
|
Publish
|
||||||
|
</div>
|
||||||
|
<div class="card-header actionMessage" style="display:none"></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="group/" method="post" onsubmit="publishTopic(this)">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">Topic</label>
|
||||||
|
<input type="text" class="form-control shadow" name="topic" placeholder="app_gitea_admin" validate=":3" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">JSON</label>
|
||||||
|
<textarea class="form-control shadow" name="data" placeholder="{...}" validate=":3"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-outline-dark">Publish</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="fad fa-users-class"></i>
|
||||||
|
Incoming Topics
|
||||||
|
</div>
|
||||||
|
<div class="card-header actionMessage" style="display:none">
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="" id="tableAJAX">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%- include('bottom') %>
|
491
package-lock.json
generated
491
package-lock.json
generated
@ -1,491 +0,0 @@
|
|||||||
{
|
|
||||||
"requires": true,
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"dependencies": {
|
|
||||||
"abbrev": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
|
||||||
},
|
|
||||||
"ansi-regex": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
|
||||||
},
|
|
||||||
"aproba": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
|
||||||
},
|
|
||||||
"are-we-there-yet": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
|
||||||
"requires": {
|
|
||||||
"delegates": "^1.0.0",
|
|
||||||
"readable-stream": "^2.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"balanced-match": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
|
||||||
},
|
|
||||||
"bcrypt": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-hSIZHkUxIDS5zA2o00Kf2O5RfVbQ888n54xQoF/eIaquU4uaLxK8vhhBdktd0B3n2MjkcAWzv4mnhogykBKOUQ==",
|
|
||||||
"requires": {
|
|
||||||
"node-addon-api": "^2.0.0",
|
|
||||||
"node-pre-gyp": "0.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"brace-expansion": {
|
|
||||||
"version": "1.1.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
|
||||||
"requires": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"chownr": {
|
|
||||||
"version": "1.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
|
||||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
|
||||||
},
|
|
||||||
"code-point-at": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
|
||||||
},
|
|
||||||
"concat-map": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
|
||||||
},
|
|
||||||
"console-control-strings": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
|
||||||
},
|
|
||||||
"core-util-is": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"version": "3.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
|
||||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"deep-extend": {
|
|
||||||
"version": "0.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
|
||||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
|
||||||
},
|
|
||||||
"delegates": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
|
||||||
},
|
|
||||||
"detect-libc": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
|
||||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
|
||||||
},
|
|
||||||
"fs-minipass": {
|
|
||||||
"version": "1.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
|
||||||
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
|
||||||
"requires": {
|
|
||||||
"minipass": "^2.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fs.realpath": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
|
||||||
},
|
|
||||||
"gauge": {
|
|
||||||
"version": "2.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
|
||||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
|
||||||
"requires": {
|
|
||||||
"aproba": "^1.0.3",
|
|
||||||
"console-control-strings": "^1.0.0",
|
|
||||||
"has-unicode": "^2.0.0",
|
|
||||||
"object-assign": "^4.1.0",
|
|
||||||
"signal-exit": "^3.0.0",
|
|
||||||
"string-width": "^1.0.1",
|
|
||||||
"strip-ansi": "^3.0.1",
|
|
||||||
"wide-align": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"glob": {
|
|
||||||
"version": "7.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
|
||||||
"requires": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.0.4",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"has-unicode": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
|
||||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
|
||||||
},
|
|
||||||
"iconv-lite": {
|
|
||||||
"version": "0.4.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
|
||||||
"requires": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ignore-walk": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
|
|
||||||
"requires": {
|
|
||||||
"minimatch": "^3.0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inflight": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
|
||||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
|
||||||
"requires": {
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"wrappy": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inherits": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
|
||||||
},
|
|
||||||
"ini": {
|
|
||||||
"version": "1.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
|
||||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
|
||||||
},
|
|
||||||
"is-fullwidth-code-point": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
|
||||||
"requires": {
|
|
||||||
"number-is-nan": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isarray": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
|
||||||
},
|
|
||||||
"minimatch": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
|
||||||
"requires": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minimist": {
|
|
||||||
"version": "1.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
|
||||||
},
|
|
||||||
"minipass": {
|
|
||||||
"version": "2.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
|
||||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "^5.1.2",
|
|
||||||
"yallist": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minizlib": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
|
||||||
"requires": {
|
|
||||||
"minipass": "^2.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mkdirp": {
|
|
||||||
"version": "0.5.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
|
||||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
|
||||||
"requires": {
|
|
||||||
"minimist": "^1.2.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"needle": {
|
|
||||||
"version": "2.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz",
|
|
||||||
"integrity": "sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "^3.2.6",
|
|
||||||
"iconv-lite": "^0.4.4",
|
|
||||||
"sax": "^1.2.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node-addon-api": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
|
|
||||||
},
|
|
||||||
"node-pre-gyp": {
|
|
||||||
"version": "0.14.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz",
|
|
||||||
"integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
|
|
||||||
"requires": {
|
|
||||||
"detect-libc": "^1.0.2",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"needle": "^2.2.1",
|
|
||||||
"nopt": "^4.0.1",
|
|
||||||
"npm-packlist": "^1.1.6",
|
|
||||||
"npmlog": "^4.0.2",
|
|
||||||
"rc": "^1.2.7",
|
|
||||||
"rimraf": "^2.6.1",
|
|
||||||
"semver": "^5.3.0",
|
|
||||||
"tar": "^4.4.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nopt": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
|
||||||
"requires": {
|
|
||||||
"abbrev": "1",
|
|
||||||
"osenv": "^0.1.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"npm-bundled": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
|
|
||||||
"requires": {
|
|
||||||
"npm-normalize-package-bin": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"npm-normalize-package-bin": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
|
||||||
},
|
|
||||||
"npm-packlist": {
|
|
||||||
"version": "1.4.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
|
|
||||||
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
|
|
||||||
"requires": {
|
|
||||||
"ignore-walk": "^3.0.1",
|
|
||||||
"npm-bundled": "^1.0.1",
|
|
||||||
"npm-normalize-package-bin": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"npmlog": {
|
|
||||||
"version": "4.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
|
||||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
|
||||||
"requires": {
|
|
||||||
"are-we-there-yet": "~1.1.2",
|
|
||||||
"console-control-strings": "~1.1.0",
|
|
||||||
"gauge": "~2.7.3",
|
|
||||||
"set-blocking": "~2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"number-is-nan": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
|
||||||
},
|
|
||||||
"object-assign": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
||||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
|
||||||
},
|
|
||||||
"once": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
|
||||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
|
||||||
"requires": {
|
|
||||||
"wrappy": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"os-homedir": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
|
||||||
},
|
|
||||||
"os-tmpdir": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
|
||||||
},
|
|
||||||
"osenv": {
|
|
||||||
"version": "0.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
|
||||||
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
|
||||||
"requires": {
|
|
||||||
"os-homedir": "^1.0.0",
|
|
||||||
"os-tmpdir": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"path-is-absolute": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
|
||||||
},
|
|
||||||
"process-nextick-args": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
|
||||||
},
|
|
||||||
"rc": {
|
|
||||||
"version": "1.2.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
|
||||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
|
||||||
"requires": {
|
|
||||||
"deep-extend": "^0.6.0",
|
|
||||||
"ini": "~1.3.0",
|
|
||||||
"minimist": "^1.2.0",
|
|
||||||
"strip-json-comments": "~2.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"readable-stream": {
|
|
||||||
"version": "2.3.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
|
||||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
|
||||||
"requires": {
|
|
||||||
"core-util-is": "~1.0.0",
|
|
||||||
"inherits": "~2.0.3",
|
|
||||||
"isarray": "~1.0.0",
|
|
||||||
"process-nextick-args": "~2.0.0",
|
|
||||||
"safe-buffer": "~5.1.1",
|
|
||||||
"string_decoder": "~1.1.1",
|
|
||||||
"util-deprecate": "~1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rimraf": {
|
|
||||||
"version": "2.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
|
||||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
|
||||||
"requires": {
|
|
||||||
"glob": "^7.1.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"safe-buffer": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
|
||||||
},
|
|
||||||
"safer-buffer": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
|
||||||
},
|
|
||||||
"sax": {
|
|
||||||
"version": "1.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
|
||||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
|
||||||
},
|
|
||||||
"semver": {
|
|
||||||
"version": "5.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
|
||||||
},
|
|
||||||
"set-blocking": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
|
||||||
},
|
|
||||||
"signal-exit": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
|
||||||
},
|
|
||||||
"string-width": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
|
||||||
"requires": {
|
|
||||||
"code-point-at": "^1.0.0",
|
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
|
||||||
"strip-ansi": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string_decoder": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "~5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strip-ansi": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
|
||||||
"requires": {
|
|
||||||
"ansi-regex": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strip-json-comments": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
|
||||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
|
||||||
},
|
|
||||||
"tar": {
|
|
||||||
"version": "4.4.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
|
|
||||||
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
|
|
||||||
"requires": {
|
|
||||||
"chownr": "^1.1.1",
|
|
||||||
"fs-minipass": "^1.2.5",
|
|
||||||
"minipass": "^2.8.6",
|
|
||||||
"minizlib": "^1.2.1",
|
|
||||||
"mkdirp": "^0.5.0",
|
|
||||||
"safe-buffer": "^5.1.2",
|
|
||||||
"yallist": "^3.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"util-deprecate": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
|
||||||
},
|
|
||||||
"wide-align": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
|
||||||
"requires": {
|
|
||||||
"string-width": "^1.0.2 || 2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"wrappy": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
|
||||||
},
|
|
||||||
"yallist": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user