mirror of
https://github.com/IrosTheBeggar/mStream.git
synced 2025-10-27 07:31:02 +00:00
1157 lines
56 KiB
HTML
1157 lines
56 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<link rel="shortcut icon" type="image/png" href="images/favicon.png"/>
|
||
<title>mStream Express Server</title>
|
||
|
||
<script src="js/materialize.min.js"></script>
|
||
<link href="css/materialize.min.css" rel="stylesheet">
|
||
|
||
<link href="css/index3.css" rel="stylesheet">
|
||
<link href="css/boot.css" rel="stylesheet">
|
||
<script src="js/vue.js"></script>
|
||
|
||
<script src="js/izi-toast.min.js"></script>
|
||
<link href="css/izi-toast.min.css" rel="stylesheet">
|
||
|
||
<link href="fonts/jura.css" rel="stylesheet">
|
||
</head>
|
||
|
||
<body>
|
||
<div class="modal">
|
||
<div id="switcherModal">
|
||
<component :key="componentKey" v-bind:is="currentViewModal">
|
||
</component>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="main">
|
||
<div class="main-left-col">
|
||
<!-- Buttons -->
|
||
<div class="left-nav-menu-header">
|
||
Server
|
||
</div>
|
||
<ul class="left-nav-menu">
|
||
<li id="nav-directories" class="left-nav-button waves-effect waves-purple nav-selected">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" height="28"><path fill="#FFA000" d="M38 12H22l-4-4H8c-2.2 0-4 1.8-4 4v24c0 2.2 1.8 4 4 4h31c1.7 0 3-1.3 3-3V16c0-2.2-1.8-4-4-4z"/><path fill="#FFCA28" d="M42.2 18H15.3c-1.9 0-3.6 1.4-3.9 3.3L8 40h31.7c1.9 0 3.6-1.4 3.9-3.3l2.5-14c.5-2.4-1.4-4.7-3.9-4.7z"/></svg>
|
||
<span>Directories</span>
|
||
</li>
|
||
<li id="nav-users" class="left-nav-button waves-effect waves-purple">
|
||
<svg xmlns="http://www.w3.org/2000/svg" height="28" viewBox="200 200 800 800"><path fill="#FFF" d="M671.027 410.286c31.749 19.852 54.013 53.574 58.044 92.642 12.928 6.092 27.325 9.578 42.596 9.578 55.593 0 100.645-45.073 100.645-100.73 0-55.591-45.052-100.706-100.645-100.706-55.152.087-99.807 44.283-100.64 99.216m-65.998 206.146c55.611 0 100.749-45.093 100.749-100.705 0-55.568-45.138-100.665-100.749-100.665-55.569 0-100.662 45.097-100.662 100.665 0 55.612 45.092 100.705 100.662 100.705m42.706 6.883H562.3c-71.084 0-128.886 57.823-128.886 128.886v104.541l.218 1.623 7.232 2.255c67.816 21.213 126.781 28.225 175.295 28.225 94.746 0 149.683-26.955 153.076-28.728l6.707-3.375h.723V752.201c0-71.063-57.827-128.886-128.93-128.886M814.33 519.32h-84.754c-.965 33.965-15.406 64.487-38.305 86.465 63.217 18.8 109.431 77.392 109.431 146.613v32.211c83.705-3.089 131.951-26.823 135.152-28.442l6.707-3.42h.744V648.273c0-71.106-57.826-128.953-128.975-128.953m-385.958-6.814c19.655 0 37.997-5.742 53.556-15.537 4.907-32.188 22.172-60.344 46.827-79.498.108-1.882.283-3.726.283-5.611 0-55.631-45.094-100.704-100.666-100.704-55.633 0-100.751 45.073-100.751 100.704.001 55.573 45.118 100.646 100.751 100.646m90.433 93.279c-22.766-21.846-37.249-52.261-38.279-85.942-3.159-.216-6.248-.523-9.468-.523h-85.455c-71.083 0-128.929 57.847-128.929 128.953v104.474l.26 1.644 7.188 2.301c54.474 17.026 103.075 24.829 145.234 27.238v-31.532c-.001-69.221 46.233-127.77 109.449-146.613"/></svg>
|
||
<span>Users</span>
|
||
</li>
|
||
<li id="nav-security" class="left-nav-button waves-effect waves-purple">
|
||
<svg xmlns="http://www.w3.org/2000/svg" height="28" viewBox="0 0 460 460"><path d="M360.228,386.747L230,460L99.772,386.747c-27.804-15.639-45.01-45.06-45.01-76.96 V21.905L230,0l175.238,21.905v287.882C405.238,341.687,388.032,371.107,360.228,386.747z" fill="#86c867"/><path d="M335.565,357.061L230,416.442l-105.565-59.38 c-22.538-12.678-36.486-36.526-36.486-62.385V54.762L230,37.006l142.051,17.756v239.915 C372.051,320.535,358.103,344.384,335.565,357.061z" fill="#5e9b3e"/><path d="M230,460l130.228-73.253c27.803-15.64,45.01-45.06,45.01-76.96V21.905 L230,0v37.005l142.051,17.756v239.915c0,25.859-13.948,49.707-36.486,62.385L230,416.442V460z" opacity=".7" fill="#86c867"/><path d="M230,98.571c-30.244,0-54.762,24.518-54.762,54.762 c0,22.454,13.519,41.74,32.857,50.192V222.2h9.701c4.308,0,7.8,3.492,7.8,7.8s-3.492,7.8-7.8,7.8h-9.701v17.257h9.701 c4.308,0,7.8,3.492,7.8,7.8c0,4.308-3.492,7.8-7.8,7.8h-9.701v17.257h9.701c4.308,0,7.8,3.492,7.8,7.8c0,4.308-3.492,7.8-7.8,7.8 h-9.701v25.057L230,339.524l21.905-10.952V203.525c19.338-8.452,32.857-27.739,32.857-50.192 C284.762,123.089,260.244,98.571,230,98.571z M230,157.167c-8.166,0-14.786-6.62-14.786-14.786c0-8.166,6.62-14.786,14.786-14.786 s14.786,6.62,14.786,14.786C244.786,150.547,238.166,157.167,230,157.167z" fill="#ecf0f1"/></svg>
|
||
<span>Security</span>
|
||
</li>
|
||
<li id="nav-network" class="left-nav-button waves-effect waves-purple">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="#FFF"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.2 5.9l.8-.8C19.6 3.7 17.8 3 16 3s-3.6.7-5 2.1l.8.8C13 4.8 14.5 4.2 16 4.2s3 .6 4.2 1.7zm-.9.8c-.9-.9-2.1-1.4-3.3-1.4s-2.4.5-3.3 1.4l.8.8c.7-.7 1.6-1 2.5-1 .9 0 1.8.3 2.5 1l.8-.8zM19 13h-2V9h-2v4H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-4c0-1.1-.9-2-2-2zM8 18H6v-2h2v2zm3.5 0h-2v-2h2v2zm3.5 0h-2v-2h2v2z"/></svg>
|
||
<span>Network</span>
|
||
</li>
|
||
<!-- <li id="nav-settings" class="left-nav-button waves-effect waves-purple">
|
||
<svg height="28" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M490.51 294.933V216.32l-57.766-11.093c-3.883-13.594-9.354-26.549-16.092-38.657l32.701-48.337-55.602-55.587-48.244 32.663c-12.309-6.885-25.492-12.177-39.338-16.109l-11.043-57.14h-78.614l-11.049 57.194c-13.895 3.956-27.123 9.4-39.463 16.338l-48.069-32.61-55.589 55.562 32.726 48.312c-6.765 12.204-12.277 25.253-16.142 38.959L21.49 216.841v78.613l57.659 11.078c3.917 13.661 9.426 26.676 16.228 38.834l-32.932 48.67 55.601 55.59 48.83-33.061c12.103 6.719 25.048 11.981 38.629 15.852l11.099 57.524h78.614l11.096-57.531c13.684-3.894 26.717-9.275 38.896-16.063l48.832 33.101 55.588-55.574-33.072-48.836c6.805-12.221 12.383-25.298 16.279-39.032l57.673-11.073m-234.535 83.084c-67.441 0-122.115-54.673-122.115-122.114s54.674-122.115 122.115-122.115S378.09 188.461 378.09 255.903s-54.674 122.114-122.115 122.114" fill="#FFF"/><path d="M254.297 378v.006c.439.006.877.01 1.316.01-.291-.002-.324-.011-1.316-.016m1.379-244.211c-.461 0-.92.004-1.379.011v.005c1.016-.004 1.062-.014 1.379-.016m.299-.001l-.086.001c67.402.047 122.113 54.702 122.113 122.114 0 67.405-54.744 122.055-122.135 122.114h.107c67.441 0 122.115-54.673 122.115-122.114s-54.673-122.115-122.114-122.115m137.8-71.143l-.014.009 55.592 55.579-32.701 48.337c6.738 12.108 12.209 25.063 16.092 38.657l57.766 11.093v78.613-78.613l-57.766-11.093a182.588 182.588 0 0 0-16.006-38.657l32.66-48.337-55.623-55.588m-98.648-40.587h-40.83 40.83l11.043 57.14c13.846 3.933 27.029 9.225 39.338 16.109l.006-.003c-12.307-6.884-25.494-12.174-39.338-16.106l-11.049-57.14" fill="#FFF"/><path d="M295.127 22.058h-40.83V133.8c.459-.007.918-.011 1.379-.011l.125-.001.088.001.086-.001c67.441 0 122.115 54.673 122.115 122.115s-54.674 122.114-122.115 122.114h-.216l-.145-.001c-.439 0-.877-.004-1.316-.01v111.936h40.92l11.096-57.531c13.684-3.894 26.717-9.275 38.896-16.063l48.832 33.101 55.588-55.574-33.072-48.836c6.805-12.221 12.383-25.298 16.279-39.032l57.674-11.073v-78.613l-57.766-11.093c-3.883-13.594-9.354-26.549-16.092-38.657l32.701-48.337-55.592-55.579-48.244 32.654-.004-.003-.006.003c-12.309-6.885-25.492-12.177-39.338-16.109l-11.043-57.142" fill="#FFF"/><path d="M256.072 111.928c-79.505 0-143.957 64.451-143.957 143.956 0 79.503 64.452 143.955 143.957 143.955 79.504 0 143.955-64.452 143.955-143.955 0-79.505-64.451-143.956-143.955-143.956zm0 217.641c-40.696 0-73.687-32.99-73.687-73.686 0-40.697 32.991-73.687 73.687-73.687 40.695 0 73.686 32.989 73.686 73.687 0 40.696-32.99 73.686-73.686 73.686z" fill="#FFF"/></svg>
|
||
<span>Settings</span>
|
||
</li> -->
|
||
</ul>
|
||
|
||
<div class="left-nav-menu-header">
|
||
Other
|
||
</div>
|
||
<ul class="left-nav-menu">
|
||
<li id="nav-auto-dns" class="left-nav-button waves-effect waves-light">
|
||
<svg xmlns="http://www.w3.org/2000/svg" height="28" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path fill="#8c9eff" d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/></svg>
|
||
<span>Auto DNS</span>
|
||
</li>
|
||
<li id="nav-about" class="left-nav-button waves-effect waves-light">
|
||
<svg xmlns="http://www.w3.org/2000/svg" height="28" viewBox="0 -1 24 24" fill="#fff"><path d="M4 2c-1.093 0-2 .907-2 2v18.406l1.719-1.687L6.438 18H20c1.093 0 2-.907 2-2V4c0-1.093-.907-2-2-2H4zm0 2h16v12H5.594l-.313.281L4 17.563V4zm7 2v2h2V6h-2zm0 3v5h2V9h-2z"/></svg>
|
||
<span>About</span>
|
||
</li>
|
||
</ul>
|
||
|
||
<!-- Boot Server Button -->
|
||
<div class="boot-server-button-wrapper">
|
||
<div class="boot-server-flex-wrapper">
|
||
<label class="autoboot-label">
|
||
<input id="boot-server-checkbox" type="checkbox" checked="checked" />
|
||
<span>Boot on startup</span>
|
||
</label>
|
||
<a id="boot-server-button" class="waves-effect waves-light btn blue accent-3">Boot Server</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="switcherMain" class="main-right-col">
|
||
<component v-bind:is="currentViewMain">
|
||
</component>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const remote = require('electron').remote;
|
||
const {ipcRenderer} = require('electron');
|
||
const app = remote.app;
|
||
const dialog = remote.require('electron').dialog;
|
||
const internalIp = require('internal-ip');
|
||
const publicIpMod = require('public-ip');
|
||
const path = require('path');
|
||
const fs = require('fs');
|
||
const Login = require('../modules/login');
|
||
const shell = require('electron').shell
|
||
|
||
var loadJson = {};
|
||
var configFile = path.join(app.getPath('userData'), 'save/server-config.json');
|
||
var editThisUser;
|
||
var bootFlag = false;
|
||
var publicIp;
|
||
|
||
(async () => {
|
||
publicIp = await publicIpMod.v4();
|
||
})();
|
||
|
||
// Open a tags in OS browser
|
||
document.addEventListener('click', function (event) {
|
||
if (event.target.tagName === 'A' && event.target.href.startsWith('http')) {
|
||
event.preventDefault();
|
||
shell.openExternal(event.target.href);
|
||
}
|
||
})
|
||
|
||
try {
|
||
if (fs.statSync(configFile).isFile()) {
|
||
loadJson = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
||
}
|
||
} catch(error) {
|
||
console.log('Failed To Load JSON');
|
||
}
|
||
|
||
if (!loadJson.users || typeof loadJson.users !== 'object') {
|
||
loadJson.users = {};
|
||
}
|
||
|
||
if (!loadJson.folders || typeof loadJson.users !== 'object') {
|
||
loadJson.folders = {};
|
||
}
|
||
|
||
if (!loadJson.ssl || typeof loadJson.ssl !== 'object') {
|
||
loadJson.ssl = {};
|
||
}
|
||
|
||
if (!loadJson.port) {
|
||
loadJson.port = 3000;
|
||
}
|
||
|
||
function hashPassword(password) {
|
||
return new Promise((resolve, reject) => {
|
||
Login.hashPassword(password, (salt, hashedPassword, err) => {
|
||
if (err) {
|
||
// return callback(false, err);
|
||
return reject('Failed to hash password');
|
||
}
|
||
resolve({salt, hashPassword: Buffer.from(hashedPassword).toString('hex')});
|
||
});
|
||
});
|
||
}
|
||
|
||
window.onload = function () {
|
||
// Initialize Modal
|
||
var modalInstance = M.Modal.init(document.querySelectorAll('.modal'), { endingTop: '20%' });
|
||
|
||
Vue.component('folder-accordion', {
|
||
data: function() {
|
||
return {
|
||
instances: null,
|
||
directories: loadJson.folders,
|
||
resetFlag: false,
|
||
closeOverride: () => {
|
||
if (this.resetFlag) {
|
||
this.$parent.componentKey = !this.$parent.componentKey;
|
||
}
|
||
}
|
||
};
|
||
},
|
||
template: '\
|
||
<ul class="z-depth-1 collapsible-folders">\
|
||
<li v-for="(value, key, index) in directories">\
|
||
<div v-on:click="toggleOptions(index, $event)" class="collapsible-header">\
|
||
<div class="accordion-header-left">\
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="32" height="100%"><path fill="#FFA000" d="M38 12H22l-4-4H8c-2.2 0-4 1.8-4 4v24c0 2.2 1.8 4 4 4h31c1.7 0 3-1.3 3-3V16c0-2.2-1.8-4-4-4z"/><path fill="#FFCA28" d="M42.2 18H15.3c-1.9 0-3.6 1.4-3.9 3.3L8 40h31.7c1.9 0 3.6-1.4 3.9-3.3l2.5-14c.5-2.4-1.4-4.7-3.9-4.7z"/></svg>\
|
||
</div>\
|
||
<div class="accordion-header-right">\
|
||
<div><b>{{key}}</b></div>\
|
||
<div>{{value.root}}</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="collapsible-body clearfix">\
|
||
<div class="folder-button-group">\
|
||
<a v-on:click="changeDirectory(value, key, index)" class="waves-effect waves-light btn">Change Directory</a>\
|
||
<a v-on:click="deleteFolder(value, key, index)" class="waves-effect waves-light btn red">\
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="100%" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="#FFF" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/><path fill="none" d="M0 0h24v24H0z"/></svg>\
|
||
</a>\
|
||
</div>\
|
||
</div>\
|
||
</li>\
|
||
</ul>',
|
||
mounted: function () {
|
||
this.instances = M.Collapsible.init(document.querySelectorAll('.collapsible-folders'), { onCloseEnd: this.closeOverride });
|
||
},
|
||
beforeDestroy: function() {
|
||
this.instances[0].destroy();
|
||
},
|
||
methods: {
|
||
changeDirectory: function(value, key, index) {
|
||
dialog.showOpenDialog({properties: [ 'openDirectory']}, (selectedDirectory) => {
|
||
if(selectedDirectory == null ){
|
||
return;
|
||
}
|
||
loadJson.folders[key] = { root: selectedDirectory[0] };
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
this.resetFlag = true;
|
||
this.instances[0].close(index);
|
||
});
|
||
},
|
||
deleteFolder: function(value, key, index) {
|
||
iziToast.question({
|
||
timeout: 20000,
|
||
close: false,
|
||
overlayClose: true,
|
||
overlay: true,
|
||
displayMode: 'once',
|
||
id: 'question',
|
||
zindex: 99999,
|
||
title: "Delete <b>'" + value.root + "'</b>?",
|
||
position: 'center',
|
||
buttons: [
|
||
['<button><b>Delete</b></button>', (instance, toast) => {
|
||
delete loadJson.folders[key];
|
||
if(loadJson.users) {
|
||
Object.keys(loadJson.users).forEach(user => {
|
||
loadJson.users[user].vpaths = loadJson.users[user].vpaths.filter(e => {
|
||
return ![key].includes(e);
|
||
});
|
||
});
|
||
}
|
||
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
this.resetFlag = true;
|
||
this.instances[0].close(index);
|
||
instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
|
||
}, true],
|
||
['<button>Go Back</button>', (instance, toast) => {
|
||
instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
|
||
}],
|
||
]
|
||
});
|
||
},
|
||
toggleOptions: function(index, el) {
|
||
if(el.target.classList.contains('btn')){
|
||
return;
|
||
}
|
||
|
||
if(el.currentTarget.parentElement.classList.contains('active')){
|
||
this.instances[0].close(index);
|
||
return;
|
||
}
|
||
|
||
this.instances[0].open(index);
|
||
}
|
||
}
|
||
});
|
||
|
||
const foldersView = Vue.component('folders-view', {
|
||
data() {
|
||
return {
|
||
componentKey: false, // Flip this value to force re-render the folder accordion thing
|
||
dirName: '' // for the input field
|
||
};
|
||
},
|
||
template: '\
|
||
<div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="section-header">Add Folder</div>\
|
||
<form id="choose-directory-form" class="choose-directory-form" @submit.prevent="addFolderDialog">\
|
||
<div class="input-field directory-name-field">\
|
||
<input @blur="maybeResetForm()" pattern="[a-zA-Z0-9-]+" v-model="dirName" id="add-directory-name" required type="text" class="validate">\
|
||
<label for="add-directory-name">Display Name</label>\
|
||
<span class="helper-text" >No special characters</span>\
|
||
</div>\
|
||
<button class="btn green waves-effect waves-light select-folder-button" type="submit">\
|
||
Select Folder\
|
||
</button>\
|
||
</form>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="section-header">Your Folders:</div>\
|
||
<template>\
|
||
<folder-accordion :key="componentKey" />\
|
||
</template>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
methods: {
|
||
maybeResetForm: function() {
|
||
if (this.dirName === '') {
|
||
document.getElementById("choose-directory-form").reset();
|
||
}
|
||
},
|
||
addFolderDialog: function (event) {
|
||
dialog.showOpenDialog({properties: [ 'openDirectory']}, (selectedDirectory) => {
|
||
if(selectedDirectory == null ){
|
||
return;
|
||
}
|
||
|
||
if (loadJson.folders[this.dirName]) {
|
||
iziToast.warning({
|
||
title: 'Display Name Already Exists',
|
||
message: 'Display names must be unique',
|
||
position: 'topCenter',
|
||
timeout: 3500
|
||
});
|
||
return;
|
||
}
|
||
|
||
loadJson.folders[this.dirName] = { root: selectedDirectory[0] };
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
this.componentKey = !this.componentKey;
|
||
this.dirName = '';
|
||
document.getElementById("choose-directory-form").reset();
|
||
});
|
||
},
|
||
}
|
||
});
|
||
|
||
const aboutView = Vue.component('about-view', {
|
||
template: '\
|
||
<div>\
|
||
<img class="mstream-logo" src="images/mstream-logo.svg">\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="about-content-section">\
|
||
<div class="about-content-header">Developed By</div>\
|
||
<div class="about-content-body">Paul Sori</div>\
|
||
<div class="about-content-body">paul@mstream.io</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
});
|
||
|
||
Vue.component('user-accordion', {
|
||
data: function() {
|
||
return {
|
||
instances: null,
|
||
users: loadJson.users,
|
||
resetFlag: false,
|
||
closeOverride: () => {
|
||
if (this.resetFlag) {
|
||
this.$parent.componentKey = !this.$parent.componentKey;
|
||
}
|
||
}
|
||
};
|
||
},
|
||
template: '\
|
||
<ul class="z-depth-1 collapsible-folders">\
|
||
<li v-for="(value, key, index) in users">\
|
||
<div v-on:click="toggleOptions(index, $event)" class="collapsible-header">\
|
||
<div class="accordion-header-left">\
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="100%" viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>\
|
||
</div>\
|
||
<div class="accordion-header-right">\
|
||
<div><b>{{key}}</b></div>\
|
||
<div class="user-folders-line">\
|
||
<?xml version="1.0" encoding="utf-8"?><svg width="24" height="22" xmlns="http://www.w3.org/2000/svg" viewBox="6 6 40 40" style="enable-background:new 0 0 48 48"><path d="M16.516 20.688C16.266 21.25 12 31.906 12 31.906V17c0-.55.45-1 1-1h1.334l.35-1.052C14.857 14.427 15.45 14 16 14h5c.55 0 1.143.427 1.316.948l.35 1.052H32c.55 0 1 .45 1 1v3H17.5c-.275 0-.734.125-.984.688zM41 21H19c-.55 0-1.167.418-1.371.929l-5.258 13.143c-.204.51.079.928.629.928h22c.55 0 1.167-.418 1.371-.929l5.258-13.143c.204-.51-.079-.928-.629-.928z"/></svg>\
|
||
{{folderList(value.vpaths)}}\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="collapsible-body clearfix">\
|
||
<div class="folder-button-group">\
|
||
<a v-on:click="openUserPasswordModal(value, key, index)" class="waves-effect waves-light btn">Reset Password</a>\
|
||
<a v-on:click="openChangeFoldersModal(value, key, index)" class="waves-effect waves-light btn">Edit</a>\
|
||
<a v-on:click="deleteUser(value, key, index)" class="waves-effect waves-light btn red">\
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="100%" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="#FFF" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/><path fill="none" d="M0 0h24v24H0z"/></svg>\
|
||
</a>\
|
||
</div>\
|
||
</div>\
|
||
</li>\
|
||
</ul>',
|
||
mounted: function () {
|
||
this.instances = M.Collapsible.init(document.querySelectorAll('.collapsible-folders'), { onCloseEnd: this.closeOverride });
|
||
},
|
||
beforeDestroy: function() {
|
||
this.instances[0].destroy();
|
||
},
|
||
methods: {
|
||
folderList: function(arr) {
|
||
var returnThis = '';
|
||
arr.forEach((element) => {
|
||
returnThis += element + ', '
|
||
});
|
||
returnThis = returnThis.slice(0, -2);
|
||
return returnThis;
|
||
},
|
||
openUserPasswordModal: function(value, key, index) {
|
||
editThisUser = key;
|
||
if (vModal.$children[0]) {
|
||
vModal.componentKey = !vModal.componentKey;
|
||
}
|
||
|
||
vModal.currentViewModal = 'user-password-view';
|
||
modalInstance[0].open();
|
||
},
|
||
openChangeFoldersModal: function(value, key, index) {
|
||
editThisUser = key;
|
||
if (vModal.$children[0]) {
|
||
vModal.componentKey = !vModal.componentKey;
|
||
}
|
||
vModal.currentViewModal = 'user-folders-view';
|
||
modalInstance[0].open();
|
||
},
|
||
deleteUser: function(value, key, index) {
|
||
iziToast.question({
|
||
timeout: 20000,
|
||
close: false,
|
||
overlayClose: true,
|
||
overlay: true,
|
||
displayMode: 'once',
|
||
id: 'question',
|
||
zindex: 99999,
|
||
title: "Delete <b>'" + key + "'</b>?",
|
||
position: 'center',
|
||
buttons: [
|
||
['<button><b>Delete</b></button>', (instance, toast) => {
|
||
delete loadJson.users[key];
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
this.resetFlag = true;
|
||
this.instances[0].close(index);
|
||
instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
|
||
}, true],
|
||
['<button>Go Back</button>', (instance, toast) => {
|
||
instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
|
||
}],
|
||
]
|
||
});
|
||
},
|
||
toggleOptions: function(index, el) {
|
||
if(el.target.classList.contains('btn')){
|
||
return;
|
||
}
|
||
|
||
if(el.currentTarget.parentElement.classList.contains('active')){
|
||
this.instances[0].close(index);
|
||
return;
|
||
}
|
||
|
||
this.instances[0].open(index);
|
||
}
|
||
}
|
||
});
|
||
|
||
const usersView = Vue.component('users-view', {
|
||
data() {
|
||
return {
|
||
directories: loadJson.folders,
|
||
componentKey: false, // Flip this value to force re-render the folder accordion thing
|
||
newUsername: '', // for the input field
|
||
selectInstance: null,
|
||
newPassword: ''
|
||
};
|
||
},
|
||
template: '\
|
||
<div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="section-header">New User</div>\
|
||
<form id="add-user-form" class="" @submit.prevent="addUser">\
|
||
<div class="row row-mod">\
|
||
<div class="input-field directory-name-field col s12 m6">\
|
||
<input @blur="maybeResetForm()" pattern="[a-zA-Z0-9-]+" v-model="newUsername" id="new-username" required type="text" class="validate">\
|
||
<label for="new-username">Username</label>\
|
||
</div>\
|
||
<div class="input-field directory-name-field col s12 m6">\
|
||
<input @blur="maybeResetForm()" v-model="newPassword" id="new-password" required type="password" class="validate">\
|
||
<label for="new-password">Password</label>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row row-mod">\
|
||
<div class="input-field col s12">\
|
||
<select :disabled="Object.keys(directories).length === 0" id="new-user-dirs" multiple>\
|
||
<option disabled selected value="" v-if="Object.keys(directories).length === 0">You must add a directory before adding a user</option>\
|
||
<option v-for="(key, value) in directories" :value="value">{{ value }}</option>\
|
||
</select>\
|
||
<label for="new-user-dirs">Select User\'s Directories</label>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row row-mod">\
|
||
<div class="col s12 m6">\
|
||
</div>\
|
||
<div class="col s12 m6">\
|
||
<button id="submit-add-user-form" class="btn green waves-effect waves-light col s6" type="submit">\
|
||
Add user\
|
||
</button>\
|
||
</div>\
|
||
</div>\
|
||
</form>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="section-header">Users:</div>\
|
||
<template>\
|
||
<user-accordion :key="componentKey" />\
|
||
</template>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
mounted: function () {
|
||
this.selectInstance = M.FormSelect.init(document.querySelectorAll("#new-user-dirs"));
|
||
},
|
||
beforeDestroy: function() {
|
||
this.selectInstance[0].destroy();
|
||
},
|
||
methods: {
|
||
maybeResetForm: function() {
|
||
if (this.newUsername === '' && this.newPassword === '' && this.selectInstance[0].getSelectedValues().length === 0) {
|
||
document.getElementById("add-user-form").reset();
|
||
}
|
||
},
|
||
addUser: function (event) {
|
||
if (this.selectInstance[0].getSelectedValues().length === 0) {
|
||
iziToast.warning({
|
||
title: 'Cannot add user without a directory',
|
||
position: 'topCenter',
|
||
timeout: 3500
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (loadJson.users[this.newUsername]) {
|
||
iziToast.warning({
|
||
title: 'User Already Exists',
|
||
position: 'topCenter',
|
||
timeout: 3500
|
||
});
|
||
return;
|
||
}
|
||
|
||
hashPassword(this.newPassword).then(hashObj => {
|
||
loadJson.users[this.newUsername] = {
|
||
vpaths: this.selectInstance[0].getSelectedValues(),
|
||
password: hashObj.hashPassword,
|
||
salt: hashObj.salt
|
||
};
|
||
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
this.newPassword = '';
|
||
this.newUsername = '';
|
||
this.selectInstance[0].input.value = '';
|
||
this.selectInstance[0].el.value = "";
|
||
this.componentKey = !this.componentKey;
|
||
|
||
document.getElementById("add-user-form").reset();
|
||
});
|
||
},
|
||
}
|
||
});
|
||
|
||
const networkView = Vue.component('network-view', {
|
||
data() {
|
||
return {
|
||
lJson: loadJson,
|
||
};
|
||
},
|
||
template: '\
|
||
<div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="row row-mod">\
|
||
<div class="section-header">Network Details</div>\
|
||
<div class="col s12">\
|
||
<div>Local IP: {{networkIp}}</div>\
|
||
<div>Public IP: {{pIp}}</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row">\
|
||
<div class="col s4">\
|
||
<div class="card">\
|
||
<div class="row row-mod">\
|
||
<div class="col s12 port-form-container">\
|
||
<div class="input-field">\
|
||
<input v-model="lJson.port" @blur="updateConfig()" id="server-port" type="number" class="validate" min="1" max="65535" step="1" required />\
|
||
<label for="server-port">Port #</label>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row">\
|
||
<div class="col s8">\
|
||
<div class="card">\
|
||
<div class="section-header">Going Online</div>\
|
||
<div class="row row-mod">\
|
||
<div class="col s12">\
|
||
Getting your server online can take some work depending on your local network. <a href="https://mstream.io/">You can read our how-to guide here.</a>\
|
||
<br><br>\
|
||
mStream Auto DNS will get your computer online effortlessly. We are now offering a free 2 weeks of our DNS services \
|
||
</div>\
|
||
<div class="col s12">\
|
||
<button v-on:click="goToAutoDns()" class="btn blue waves-effect waves-light auto-dns-link-button">\
|
||
Check Out Auto DNS\
|
||
</button>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
computed: {
|
||
networkIp: function () {
|
||
// `this` points to the vm instance
|
||
return internalIp.v4.sync();
|
||
},
|
||
pIp: function() {
|
||
return publicIp;
|
||
}
|
||
},
|
||
methods: {
|
||
updateConfig: function() {
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
},
|
||
goToAutoDns: function() {
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'auto-dns-view';
|
||
resetNav(document.getElementById("nav-auto-dns"));
|
||
}
|
||
}
|
||
});
|
||
|
||
const securityView = Vue.component('security-view', {
|
||
data: function() {
|
||
return {
|
||
currentViewSsl: (!loadJson.ssl.cert && !loadJson.ssl.key) ? 'security-view-signup' : 'security-view-diy'
|
||
};
|
||
},
|
||
template: '\
|
||
<div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="section-header">SSL Encryption</div>\
|
||
<div class="row row-mod">\
|
||
<div class="col s12">\
|
||
This feature is not enabled by default. You must provide your own SSL certificates to enable encryption. SSL certificates can be registered for free with <a href="https://certbot.eff.org/">Certbot</a>\
|
||
<br><br>\
|
||
mStream Auto DNS takes care of registering certificates and configuring server encryption. <a href="https://mstream.io">Read more about it here</a>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row">\
|
||
<div class="col s12 security-container">\
|
||
<transition name="component-fade" mode="out-in">\
|
||
<component v-bind:is="currentViewSsl">\
|
||
</component>\
|
||
</transition>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
});
|
||
|
||
const securityViewDiyKeys = Vue.component('security-view-diy', {
|
||
data: function() {
|
||
return {
|
||
ssl: loadJson.ssl
|
||
};
|
||
},
|
||
template: '\
|
||
<div class="card security-diy">\
|
||
<svg v-on:click="clearSSL($event)" class="remove-ssl" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#e53935" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/><path d="M0 0h24v24H0z" fill="none"/></svg>\
|
||
<div class="section-header">SSL Credentials</div>\
|
||
<div class="row row-mod">\
|
||
<div class="col s12">\
|
||
<div class="input-field">\
|
||
<input v-model="ssl.key" v-on:click="selectKeyFile($event)" id="server-ssl-key" type="text">\
|
||
<label for="server-ssl-key">Key File</label>\
|
||
</div>\
|
||
</div>\
|
||
<div class="col s12">\
|
||
<div class="input-field">\
|
||
<input v-model="ssl.cert" v-on:click="selectCertFile($event)" id="server-ssl-cert" type="text">\
|
||
<label for="server-ssl-cert">Certificate</label>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
mounted: function() {
|
||
M.updateTextFields();
|
||
},
|
||
methods: {
|
||
clearSSL: function() {
|
||
loadJson.ssl.key = '';
|
||
loadJson.ssl.cert = '';
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
M.updateTextFields();
|
||
this.$parent._data.currentViewSsl = 'security-view-signup';
|
||
},
|
||
selectKeyFile: function(el) {
|
||
dialog.showOpenDialog({properties: [ 'openFile']}, (selectedFile) => {
|
||
if(selectedFile == null ){
|
||
return;
|
||
}
|
||
el.target.value = selectedFile;
|
||
loadJson.ssl.key = selectedFile;
|
||
el.target.blur();
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
});
|
||
},
|
||
selectCertFile: function(el) {
|
||
dialog.showOpenDialog({properties: [ 'openFile']}, (selectedFile) => {
|
||
if(selectedFile == null ){
|
||
return;
|
||
}
|
||
el.target.value = selectedFile;
|
||
loadJson.ssl.cert = selectedFile;
|
||
el.target.blur();
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
});
|
||
},
|
||
}
|
||
});
|
||
|
||
const securityViewLoggedIn = Vue.component('security-view-logged-in', {
|
||
template: '\
|
||
<div class="ssl-overlay card">\
|
||
<div id="logged-in-panel" class="logged-in-panel">\
|
||
<img class="logged-in-checkmark" src="images/checkmark.svg">\
|
||
<div>\
|
||
<p class="bigger-text">Logged In</p>\
|
||
<p>Domain: <span id="your-domain">LOADING...</span> </br>\
|
||
IP Address: <span id="ip-status">LOADING...</span> </br>\
|
||
Certs: <span id="cert-status">LOADING...</span> </br>\
|
||
</p>\
|
||
</div>\
|
||
</div>\
|
||
</div>'
|
||
});
|
||
|
||
const securityViewSignup = Vue.component('security-view-signup', {
|
||
data: function() {
|
||
return {
|
||
ssl: loadJson.ssl
|
||
};
|
||
},
|
||
template: '\
|
||
<div class="ssl-choice card no-pad-bot">\
|
||
<div v-on:click="goToAutoDns()" class="signup-button waves-effect waves-default">\
|
||
<p class="center bigger-text"><b>Auto DNS</b></p>\
|
||
<svg height="88" width="100%" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><path d="M46.698 33.004H31.99c-3.306 0-5.986 2.68-5.986 5.986v8.295c0 3.637 2.948 6.585 6.585 6.585H46.1c3.637 0 6.585-2.948 6.585-6.585V38.99C52.684 35.684 50.004 33.004 46.698 33.004zM49.547 47.588c0 1.93-1.564 3.494-3.494 3.494H32.635c-1.93 0-3.494-1.564-3.494-3.494v-8.302c0-1.93 1.564-3.494 3.494-3.494h13.419c1.93 0 3.494 1.564 3.494 3.494V47.588zM31.99 31.935h2.01V28.42c0-2.947 2.398-5.345 5.345-5.345 2.947 0 5.345 2.398 5.345 5.345v3.515h2.01c.574 0 1.133.071 1.668.201V28.42c0-4.975-4.047-9.022-9.022-9.022s-9.022 4.047-9.022 9.022v3.716C30.857 32.006 31.415 31.935 31.99 31.935z" fill="#3E4753"/><path d="M45.095,37.451H33.593c-1.287,0-2.331,1.043-2.331,2.331v7.311c0,1.287,1.043,2.331,2.331,2.331h11.503 c1.287,0,2.331-1.043,2.331-2.331v-7.311C47.426,38.494,46.382,37.451,45.095,37.451z M40.705,43.86v2.064 c0,0.748-0.612,1.361-1.361,1.361c-0.748,0-1.361-0.612-1.361-1.361V43.86c-0.599-0.426-0.991-1.126-0.991-1.918 c0-1.299,1.053-2.353,2.353-2.353c1.299,0,2.353,1.054,2.353,2.353C41.697,42.734,41.305,43.434,40.705,43.86z" fill="#2E7EB8"/><g><path d="M60.7,29.153c-1.119-1.406-2.581-2.503-4.224-3.19c-0.306-4.111-2.037-7.917-4.952-10.832 c-3.225-3.225-7.542-5.002-12.154-5.002c-5.136,0-9.986,2.323-13.291,6.292c-1.267-0.358-2.555-0.539-3.848-0.539 c-6.201,0-11.544,3.92-13.466,9.701C6.783,26.166,5,27.308,3.638,28.889C1.937,30.865,1,33.402,1,36.035 c0,5.957,4.846,10.803,10.803,10.803h2.839h8.154V42.25h-8.154h-2.839c-3.427,0-6.215-2.788-6.215-6.215 c0-3.173,2.246-5.796,5.341-6.238l1.571-0.224l0.344-1.549c0.988-4.446,4.848-7.552,9.386-7.552c1.27,0,2.546,0.263,3.792,0.782 l1.731,0.721l1.051-1.554c2.416-3.571,6.365-5.703,10.564-5.703c7.047,0,12.567,5.52,12.567,12.567v2.193l1.789,0.404 c2.842,0.642,4.768,3.145,4.684,6.086l-0.001,0.066c0,2.041-0.993,3.85-2.517,4.984v5.154c4.128-1.511,7.091-5.462,7.105-10.105 C63.061,33.55,62.246,31.095,60.7,29.153z" fill="#2E7EB8"/></g></svg>\
|
||
<p class="signup-price">Sign Up / Login</p>\
|
||
</div>\
|
||
<div v-on:click="goToDiy()" class="diy-button clearfix waves-effect waves-default">\
|
||
<svg class="ssl-svg3 center" height="60" width="100%" viewBox="0 0 33 33" xmlns="http://www.w3.org/2000/svg"><path d="M13.641 8.239c2.021-1.023 3.667-2.107 4.591-2.76 1.732 1.223 5.988 3.967 10.535 4.972-.121 1.343-.432 3.75-1.211 6.384l2.445 1.14c1.351-4.386 1.468-8.24 1.476-8.577l.026-1.163-1.152-.17C24.813 7.249 19.1 2.848 19.043 2.805l-.81-.631-.81.63C17.387 2.833 15 4.67 11.825 6.192 12.553 6.762 13.167 7.456 13.641 8.239zM18.232 28.08c-4.332-1.375-6.896-4.98-8.415-8.664-.851.328-1.754.504-2.677.514 1.758 4.531 4.947 9.158 10.734 10.795l.357.102.359-.102c3.113-.881 5.475-2.629 7.267-4.76l-2.523-1.178C21.988 26.24 20.316 27.418 18.232 28.08z" fill="#2B79C2"/><path d="M26.357 16.275c.527-1.839.818-3.558.979-4.827-3.756-1.059-7.178-3.089-9.104-4.373-.992.662-2.383 1.521-4.011 2.344.172.443.307.904.396 1.382L26.357 16.275zM18.232 26.688c1.516-.551 2.782-1.422 3.845-2.486l-11.01-5.135C12.42 22.287 14.627 25.379 18.232 26.688z" fill="#2B79C2"/><path d="M30.555,19.479l-16.966-7.912c-0.23-2.219-1.58-4.266-3.749-5.276C7.375,5.141,4.561,5.655,2.662,7.37 L7.616,9.68l0.639,3.115l-2.797,1.512l-4.953-2.311c-0.094,2.557,1.322,5.042,3.787,6.193c2.168,1.01,4.604,0.729,6.451-0.52 l16.966,7.91c1.686,0.787,3.688,0.057,4.475-1.627C32.969,22.268,32.24,20.264,30.555,19.479z M28.001,23.615 c-0.731-0.34-1.048-1.211-0.707-1.943s1.212-1.049,1.943-0.707s1.049,1.211,0.708,1.943C29.604,23.641,28.733,23.957,28.001,23.615 z" fill="#454C51"/></svg>\
|
||
<div><p class="center bigger-text"><b>DIY</b></p></div>\
|
||
</div>\
|
||
</div>',
|
||
methods: {
|
||
goToDiy: function() {
|
||
this.$parent._data.currentViewSsl = 'security-view-diy';
|
||
},
|
||
goToAutoDns: function() {
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'auto-dns-view';
|
||
resetNav(document.getElementById("nav-auto-dns"));
|
||
}
|
||
}
|
||
});
|
||
|
||
const settingsView = Vue.component('settings-view', {
|
||
template: '\
|
||
<div>\
|
||
Settings\
|
||
</div>',
|
||
});
|
||
|
||
const userPasswordView = Vue.component('user-password-view', {
|
||
data() {
|
||
return {
|
||
users: loadJson.users,
|
||
currentUser: editThisUser,
|
||
resetPassword: '',
|
||
componentKey: false,
|
||
};
|
||
},
|
||
template: '\
|
||
<form @submit.prevent="updatePassword" id="reset-password-form">\
|
||
<div class="modal-content">\
|
||
<h4>Password Reset </h4>\
|
||
<p>User: <b>{{currentUser}}</b></p>\
|
||
<div class="input-field directory-name-field">\
|
||
<input @blur="maybeResetForm()" v-model="resetPassword" id="reset-password" required type="password" class="validate">\
|
||
<label for="reset-password">New Password</label>\
|
||
</div>\
|
||
</div>\
|
||
<div class="modal-footer">\
|
||
<a href="#!" class="modal-close waves-effect waves-green btn-flat">Go Back</a>\
|
||
<button id="submit-reset-password-form" class="btn green waves-effect waves-light" type="submit">\
|
||
Update Password\
|
||
</button>\
|
||
</div>\
|
||
</form>',
|
||
methods: {
|
||
maybeResetForm: function() {
|
||
if (this.resetPassword === '') {
|
||
document.getElementById("reset-password-form").reset();
|
||
}
|
||
},
|
||
updatePassword: function() {
|
||
hashPassword(this.resetPassword).then(hashObj => {
|
||
loadJson.users[this.currentUser].password = hashObj.hashPassword;
|
||
loadJson.users[this.currentUser].salt = hashObj.salt;
|
||
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
this.resetPassword = '';
|
||
|
||
document.getElementById("reset-password-form").reset();
|
||
modalInstance[0].close();
|
||
|
||
iziToast.success({
|
||
title: 'Password Updated',
|
||
position: 'topCenter',
|
||
timeout: 2500
|
||
});
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
const userFoldersView = Vue.component('user-folders-view', {
|
||
data() {
|
||
return {
|
||
directories: loadJson.folders,
|
||
users: loadJson.users,
|
||
currentUser: editThisUser,
|
||
selectInstance: null,
|
||
};
|
||
},
|
||
template: '\
|
||
<form @submit.prevent="updateFolders" id="update-folders-form">\
|
||
<div class="modal-content">\
|
||
<h4>User Folders</h4>\
|
||
<p>User: <b>{{currentUser}}</b></p>\
|
||
<select :disabled="Object.keys(directories).length === 0" id="edit-user-dirs" multiple>\
|
||
<option disabled selected value="" v-if="Object.keys(directories).length === 0">You must add a directory before adding a user</option>\
|
||
<option :selected="users[currentUser].vpaths.includes(value)" v-for="(key, value) in directories" :value="value">{{ value }}</option>\
|
||
</select>\
|
||
</div>\
|
||
<div class="modal-footer">\
|
||
<a href="#!" class="modal-close waves-effect waves-green btn-flat">Go Back</a>\
|
||
<button id="submit-reset-password-form" class="btn green waves-effect waves-light" type="submit">\
|
||
Update\
|
||
</button>\
|
||
</div>\
|
||
</form>',
|
||
mounted: function () {
|
||
this.selectInstance = M.FormSelect.init(document.querySelectorAll("#edit-user-dirs"));
|
||
},
|
||
beforeDestroy: function() {
|
||
this.selectInstance[0].destroy();
|
||
},
|
||
methods: {
|
||
maybeResetForm: function() {
|
||
|
||
},
|
||
updateFolders: function() {
|
||
loadJson.users[this.currentUser].vpaths = this.selectInstance[0].getSelectedValues();
|
||
fs.writeFileSync(configFile, JSON.stringify(loadJson), 'utf8');
|
||
modalInstance[0].close();
|
||
|
||
iziToast.success({
|
||
title: 'User Updated',
|
||
position: 'topCenter',
|
||
timeout: 2500
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
const bootView = Vue.component('boot-view', {
|
||
template: "\
|
||
<div>\
|
||
<div id='bars'>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
<div class='bar'></div>\
|
||
</div>\
|
||
<div id='boot-server-text' class='boot-server-text'>\
|
||
Booting Server\
|
||
</div>\
|
||
<div id='post-boot-content'></div>\
|
||
</div>",
|
||
});
|
||
|
||
const autoDnsView = Vue.component('auto-dns-view', {
|
||
data() {
|
||
return {
|
||
username: '',
|
||
password: '',
|
||
};
|
||
},
|
||
template: '\
|
||
<div>\
|
||
<div class="row auto-dns-selection">\
|
||
<div class="col m6 s12 auto-dns-selection-item">\
|
||
<div v-on:click="goToSignUp()" class="card auto-dns-card auto-dns-sign-up-item">\
|
||
<div class="section-header">Sign Up</div>\
|
||
<svg viewBox="0 0 128 128" height="120" width="100%" xmlns="http://www.w3.org/2000/svg"><path d="M94.894,68.493c-1.379-4.803-5.663-8.043-10.661-8.063h-0.631c0.146-3.979-0.709-7.176-2.677-10.032 c-2.403-3.498-5.605-5.677-9.788-6.663c-1.014-0.237-2.09-0.357-3.197-0.357c-4.143,0-9.78,1.83-13.333,6.942 c-1.344-0.545-2.772-0.822-4.249-0.822c-5.846,0-10.765,4.5-11.299,10.287c-0.633,0.333-1.228,0.719-1.773,1.149 c-1.67,1.311-2.967,3.049-3.752,5.025c-1.284,3.234-1.095,6.848,0.518,9.914c1.72,3.263,4.408,5.412,7.774,6.215l0.023-0.096 c0.691,0.135,2.776,0.148,9.075,0.188l5.487,0.037l0.012,0.071l0.348,0.003c3.083,0.029,9.591,0.055,15.166,0.076l4.354,0.018 l-0.024,0.042l6.333,0.045c0.85,0,1.507-0.01,2.126-0.029c3.362-0.11,6.442-1.754,8.451-4.513 C95.189,75.181,95.816,71.743,94.894,68.493z M43.606,62.33c-0.108-0.479-0.162-0.98-0.162-1.494c0-3.807,3.102-6.904,6.915-6.904 c1.844,0,3.579,0.724,4.884,2.039l0.184,0.186l0.247-0.087c0.697-0.244,1.267-0.758,1.565-1.407 c0.272-0.583,0.566-1.105,0.901-1.598c2.562-3.852,6.742-5.224,9.8-5.224c0.748,0,1.483,0.08,2.181,0.239 c3.054,0.717,5.386,2.302,7.132,4.846c1.572,2.293,2.148,5.027,1.813,8.601l-0.039,0.401c-0.078,0.745,0.174,1.503,0.693,2.082 c0.514,0.561,1.241,0.884,1.998,0.884h2.496c3.005,0.01,5.571,1.949,6.385,4.831c0.55,1.897,0.177,3.933-1.022,5.587 c-1.191,1.632-3.01,2.611-4.991,2.682c-0.573,0.019-1.188-0.026-1.983,0.019H45.094c-3.293-0.01-5.615-1.387-7.097-4.216 c-1.002-1.913-1.118-4.172-0.32-6.194c0.504-1.246,1.32-2.342,2.346-3.154l0.12-0.089c0.522-0.389,1.062-0.703,1.594-0.927 c0.036-0.013,0.109-0.031,0.261-0.073c0.359-0.102,0.902-0.256,1.423-0.587l0.247-0.156L43.606,62.33z" fill="#2E79BE"/><path d="M116.277,93.951c0.098-0.469,0.137-0.958,0.137-1.447v-59.98c0-5.357-4.047-9.677-9.032-9.677h-1.916 h-4.086H20.638c-4.985,0-9.032,4.321-9.032,9.677v59.98c0,0.489,0.039,0.977,0.098,1.447H2.535v1.505 c0,5.376,4.027,9.697,9.012,9.697h104.906c4.966,0,9.013-4.321,9.013-9.697v-1.505H116.277z M71.194,100.246 c0,1.036-0.684,1.877-1.564,1.877H58.37c-0.88,0-1.564-0.821-1.564-1.877v-1.212c0-1.056,0.684-1.896,1.564-1.896H69.63 c0.88,0,1.564,0.86,1.564,1.896V100.246z M107.577,93.951H20.423V32.368h79.093h3.882h4.179V93.951z" fill="#2D3E4F"/></svg>\
|
||
</div>\
|
||
</div>\
|
||
<div class="col m6 s12 auto-dns-selection-item">\
|
||
<div class="card auto-dns-card">\
|
||
<div class="section-header">Login</div>\
|
||
<form id="login-form" class="" @submit.prevent="login">\
|
||
<div class="row row-mod">\
|
||
<div class="input-field directory-name-field col s12">\
|
||
<input @blur="maybeResetForm()" v-model="username" id="login-username" required type="text" class="validate">\
|
||
<label for="login-username">Username</label>\
|
||
</div>\
|
||
<div class="input-field directory-name-field col s12">\
|
||
<input @blur="maybeResetForm()" v-model="password" id="login-password" required type="password" class="validate">\
|
||
<label for="login-password">Password</label>\
|
||
</div>\
|
||
<div class="col s12">\
|
||
<button class="btn green waves-effect waves-light login-button" type="submit">\
|
||
Login\
|
||
</button>\
|
||
</div>\
|
||
</div>\
|
||
</form>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="row row-mod">\
|
||
<div class="section-header">mStream Auto DNS</div>\
|
||
<div class="col s12">\
|
||
<ul class="browser-default">\
|
||
<li>Get your own personal domain @ https://your-name.mstream.io</li>\
|
||
<li>Automatically configures SSL Encryption for your server</li>\
|
||
<li>State of the art \'Hole Punching\' software guarantees your server stays online as long as you have a working internet connection</li>\
|
||
<li>Auto DNS software can be re-used for any self-hosted service. You can use it host your own blog, store, or chat service right our of your own home!</li>\
|
||
</ul>\
|
||
mStream Auto DNS comes with everything you need to get your new server online in seconds. Think of it as your private cloud.\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>',
|
||
methods: {
|
||
maybeResetForm: function() {
|
||
if (this.username === '' && this.password === '') {
|
||
document.getElementById("login-form").reset();
|
||
}
|
||
},
|
||
goToSignUp: function() {
|
||
shell.openExternal('https://mstream.io');
|
||
},
|
||
login: function() {
|
||
iziToast.warning({
|
||
title: 'Incorrect Username or Password',
|
||
position: 'topCenter',
|
||
timeout: 3500
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
var vModal = new Vue({
|
||
el: '#switcherModal',
|
||
components: {
|
||
'user-password-view': userPasswordView,
|
||
'user-folders-view': userFoldersView,
|
||
},
|
||
data: {
|
||
componentKey: false,
|
||
currentViewModal: false
|
||
}
|
||
});
|
||
|
||
var vm = new Vue({
|
||
el: '#switcherMain',
|
||
components: {
|
||
'folders-view': foldersView,
|
||
'about-view': aboutView,
|
||
'users-view': usersView,
|
||
'network-view': networkView,
|
||
'security-view': securityView,
|
||
'settings-view': settingsView,
|
||
'boot-view': bootView,
|
||
'auto-dns-view': autoDnsView
|
||
},
|
||
data: {
|
||
currentViewMain: false
|
||
}
|
||
});
|
||
|
||
vm.currentViewMain = 'folders-view';
|
||
|
||
document.getElementById("nav-directories").onclick = function(){
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'folders-view';
|
||
resetNav(this);
|
||
}
|
||
|
||
document.getElementById("nav-about").onclick = function(){
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'about-view';
|
||
resetNav(this);
|
||
}
|
||
|
||
document.getElementById("nav-users").onclick = function(){
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'users-view';
|
||
resetNav(this);
|
||
}
|
||
|
||
document.getElementById("nav-network").onclick = function(){
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'network-view';
|
||
resetNav(this);
|
||
}
|
||
|
||
document.getElementById("nav-security").onclick = function(){
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'security-view';
|
||
resetNav(this);
|
||
}
|
||
|
||
document.getElementById("nav-auto-dns").onclick = function(){
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
vm.currentViewMain = 'auto-dns-view';
|
||
resetNav(this);
|
||
}
|
||
|
||
function resetNav(that) {
|
||
document.querySelectorAll('.left-nav-button').forEach((el) => {
|
||
el.classList.remove("nav-selected");
|
||
});
|
||
|
||
if (that) {
|
||
that.classList.add('nav-selected');
|
||
}
|
||
}
|
||
|
||
document.getElementById("boot-server-button").onclick = function(){
|
||
if(!loadJson.folders) {
|
||
iziToast.warning({
|
||
title: 'You must set tat least one folder',
|
||
position: 'topCenter',
|
||
timeout: 3500
|
||
});
|
||
resetNav(document.getElementById("nav-directories"));
|
||
vm.currentViewMain = 'folders-view';
|
||
return;
|
||
}
|
||
|
||
if(!loadJson.port) {
|
||
iziToast.warning({
|
||
title: 'You must set the port before booting the server',
|
||
position: 'topCenter',
|
||
timeout: 3500
|
||
});
|
||
resetNav(document.getElementById("nav-network"));
|
||
vm.currentViewMain = 'network-view';
|
||
return;
|
||
}
|
||
|
||
if(bootFlag === true) {
|
||
return;
|
||
}
|
||
|
||
vm.currentViewMain = 'boot-view';
|
||
document.getElementById("boot-server-button").innerHTML = 'Booting...'
|
||
bootFlag = true;
|
||
resetNav();
|
||
|
||
if (document.getElementById("boot-server-checkbox").checked === true) {
|
||
loadJson.autoboot = true;
|
||
}
|
||
|
||
setTimeout(() => {
|
||
ipcRenderer.send('start-server', loadJson);
|
||
setTimeout(() => {
|
||
document.getElementById("boot-server-button").innerHTML = 'Success';
|
||
document.getElementById("boot-server-text").innerHTML = 'Server Booted!';
|
||
|
||
// document.getElementById("post-boot-content").innerHTML = '<div>This window will automatically close in <span id="countdown">30</span> seconds to save on memory</div>';
|
||
const localhostAdd = loadJson.ssl && loadJson.ssl.cert && loadJson.ssl.key ? 'https' : 'http' + '://localhost:' + loadJson.port;
|
||
document.getElementById("post-boot-content").innerHTML = '<div class="row">\
|
||
<div class="col s12">\
|
||
<div class="card">\
|
||
<div class="about-content-section">\
|
||
<div>This window will automatically close in <span id="countdown">30</span> seconds to save on memory</div>\
|
||
<br>\
|
||
<div>Test mStream locally at: <a href="'+localhostAdd+'">'+localhostAdd+'</a></div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
</div>';
|
||
|
||
var countdown = 30;
|
||
setInterval(() => {
|
||
countdown--;
|
||
if(countdown === 0){
|
||
var window = remote.getCurrentWindow();
|
||
window.close();
|
||
}else{
|
||
document.getElementById("countdown").innerHTML = countdown;
|
||
}
|
||
}, 1000);
|
||
}, 2500);
|
||
}, 2500);
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |