Compare commits

..

No commits in common. "082a55ea9f6e8f3442ff8b9f37ed2c5d270b997f" and "815b5a6b6a5d8e21a39555019156a463d1275268" have entirely different histories.

38 changed files with 363 additions and 1994 deletions

View file

@ -1,5 +1,3 @@
# daster # daster
Dialplan Asterisk - управление диалпланом Управление диалпланом
![mainpage](images/mainpage.png)

View file

@ -1,38 +1,38 @@
create table if not exists da_groups ( create table if not exists da_groups (
da_name varchar(20) not null, da_name varchar(20) not null,
da_comment varchar(100) default null, da_comment varchar(100) default null,
constraint da_groups_pk primary key (da_name) constraint da_groups_pk primary key (da_name)
); );
insert into da_groups (da_name, da_comment) insert into da_groups (da_name, da_comment)
values values
('general', 'Общие контакты'), ('general', 'Общие контакты'),
('work', 'Рабочие контакты'), ('work', 'Рабочие контакты'),
('personal', 'Личные контакты'); ('personal', 'Личные контакты');
create table if not exists da_lists ( create table if not exists da_lists (
da_name varchar(20) not null, da_name varchar(20) not null,
da_comment varchar(100) default null, da_comment varchar(100) default null,
constraint da_lists_pk primary key (da_name) constraint da_lists_pk primary key (da_name)
); );
insert into da_lists (da_name, da_comment) insert into da_lists (da_name, da_comment)
values values
('general', 'Общий'), ('general', 'Общий'),
('whitelist', 'Белый'), ('whitelist', 'Белый'),
('blacklist', 'Черный'); ('blacklist', 'Черный');
create table if not exists da_numbers ( create table if not exists da_numbers (
da_number varchar(12) not null, da_number varchar(12) not null,
da_group varchar(20) not null default 'general', da_group varchar(20) not null default 'general',
da_list varchar(20) not null default 'general', da_list varchar(20) not null default 'general',
da_all_cc int not null default 0, da_all_cc int not null default 0,
da_white_cc int not null default 0, da_white_cc int not null default 0,
da_black_cc int not null default 0, da_black_cc int not null default 0,
da_comment varchar(100) default null, da_comment varchar(100) default null,
constraint da_numbers_pk primary key (da_number), constraint da_numbers_pk primary key (da_number),
foreign key (da_group) references da_groups (da_name) on delete set null on update cascade, foreign key (da_group) references da_groups (da_name) on delete set null on update cascade,
foreign key (da_list) references da_lists (da_name) on delete set null on update cascade foreign key (da_list) references da_lists (da_name) on delete set null on update cascade
); );
create table if not exists da_sms ( create table if not exists da_sms (
@ -44,21 +44,6 @@ create table if not exists da_sms (
constraint da_sms_pk primary key (da_id) constraint da_sms_pk primary key (da_id)
); );
create table if not exists da_ussd_type (
da_id bigserial not null,
da_comment varchar(100) not null,
constraint da_ussd_type_pk primary key (da_id)
);
insert into da_ussd_type (da_id, da_comment)
values
('0', 'Уведомление'),
('1', 'Запрос'),
('2', 'Прервано сетью'),
('3', 'Ответ другого локального клиента'),
('4', 'Операция не поддерживается'),
('5', 'Тайм-аут сети');
create table if not exists da_ussd ( create table if not exists da_ussd (
da_id bigserial not null, da_id bigserial not null,
da_date timestamp not null default NOW(), da_date timestamp not null default NOW(),
@ -69,10 +54,9 @@ create table if not exists da_ussd (
); );
create table if not exists da_server ( create table if not exists da_server (
da_id int not null default 1, da_address varchar(50) not null,
da_transparent_mode bool not null default false, da_transparent_mode bool not null default false,
da_internal_number varchar(12) not null, da_internal_number varchar(12) not null,
da_external_number varchar(12) not null, da_external_number varchar(12) not null,
da_external_number_on bool not null default true, constraint da_server_pk primary key (da_address)
constraint da_server_pk primary key (da_id)
); );

View file

@ -5,9 +5,10 @@
"copyright": "Copyright © 2023, Alexander Zhirov", "copyright": "Copyright © 2023, Alexander Zhirov",
"dependencies": { "dependencies": {
"vibe-d": "~>0.9", "vibe-d": "~>0.9",
"singlog": "~>0.4.0", "ldap": "~>0.4",
"singlog": "~>0.3.1",
"arsd-official:postgres": "~>10.9.10", "arsd-official:postgres": "~>10.9.10",
"readconf": "~>0.4.0" "readconf": "~>0.3.1"
}, },
"buildTypes": { "buildTypes": {
"debug": { "debug": {
@ -25,7 +26,7 @@
} }
}, },
"description": "Dialplan Asterisk - веб-сервер для управления обработкой вызовов Asterisk", "description": "Dialplan Asterisk - веб-сервер для управления обработкой вызовов Asterisk",
"license": "GPL-2.0", "license": "proprietary",
"name": "daster", "name": "daster",
"targetPath": "bin", "targetPath": "bin",
"targetType": "executable" "targetType": "executable"

View file

@ -5,14 +5,15 @@
"datefmt": "1.0.4", "datefmt": "1.0.4",
"diet-ng": "1.8.1", "diet-ng": "1.8.1",
"eventcore": "0.9.25", "eventcore": "0.9.25",
"ldap": "0.4.0",
"libasync": "0.8.6", "libasync": "0.8.6",
"memutils": "1.0.9", "memutils": "1.0.9",
"mir-linux-kernel": "1.0.1", "mir-linux-kernel": "1.0.1",
"openssl": "3.3.0", "openssl": "3.3.0",
"openssl-static": "1.0.2+3.0.8", "openssl-static": "1.0.2+3.0.8",
"readconf": "0.4.0", "readconf": "0.3.1",
"silly": "1.2.0-dev.2", "silly": "1.1.1",
"singlog": "0.4.0", "singlog": "0.3.1",
"stdx-allocator": "2.77.5", "stdx-allocator": "2.77.5",
"taggedalgebraic": "0.11.22", "taggedalgebraic": "0.11.22",
"vibe-core": "2.2.0", "vibe-core": "2.2.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

View file

@ -1,48 +0,0 @@
$(document).ready(function () {
noticer = new Noticer;
$("#authorization").button({ icon: "ui-icon-home" });
$("#login, #password").on('keypress',function(e) {
if(e.which == 13) {
authorization()
}
});
$("#authorization").click(() => {
authorization()
});
$("body").fadeTo(500, 1);
});
function authorization() {
request().then((data) => {
data.error ? noticer.error(data.message) : (window.location.href = ".");
}).catch((e) => {
noticer.error(e.message);
});
}
async function request() {
let login = $("#login").val();
let password = $("#password").val();
let response = await fetch('.', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify({
login: login,
password: password,
query: "login"
})
});
if (!response.ok)
throw new Error(`Произошла неизвестаня ошибка: ${response.status}`);
const data = await response.json();
return data;
}

100
js/message.js Normal file
View file

@ -0,0 +1,100 @@
class Message {
timer;
constructor() {
this.div = $('<div id="div-message"></div>');
this.div.css({
"position": "absolute",
"top": "10px",
"right": "20px",
"z-index": "1000"
});
$('body').append(this.div);
}
success(message, delay = 6000) {
this.print(message, delay, '#52b818', '#bffdc0');
}
warning(message, delay = 6000) {
this.print(message, delay, '#b8ae18', '#f8fdbf');
}
error(message, delay = 6000) {
this.print(message, delay, '#b96161', '#fddede');
}
print = function(message, delay, border, background) {
if (delay < 6000) delay = 6000;
let Timer = function(callback, delay) {
let timerId, start, remaining = delay;
this.pause = function() {
clearTimeout(timerId);
remaining -= new Date() - start;
};
this.resume = function() {
start = new Date();
clearTimeout(timerId);
timerId = setTimeout(callback, remaining);
};
this.dead = function() {
clearTimeout(timerId);
};
this.resume();
}
let newMessage = $(`<div class="message">${message}</div>`);
newMessage.css({
"border": `1px solid ${border}`,
"background-color": `${background}`,
"color": "#333",
"padding": "10px 30px",
"text-align": "center",
"display": "none",
"margin": "10px 0 0 0",
"width": "350px",
"opacity": "1",
"cursor": "pointer"
});
newMessage.hover(function(){
$(this).css({
"opacity": "1"
});
}, function(){
$(this).css({
"opacity": "0.3"
});
});
this.div.append(newMessage);
let opacityTimeout = setTimeout(function() {
newMessage.fadeTo(1000, 0.3);
}, 2500);
newMessage.fadeIn(500).mouseenter(() => {
clearTimeout(opacityTimeout);
this.timer.pause();
}).mouseleave(() => {
this.timer.resume();
}).click(() => {
this.timer.dead();
newMessage.fadeOut(0, function(){
this.remove();
});
});
this.timer = new Timer(() => {
newMessage.fadeOut(500, function(){
this.remove();
});
}, delay);
}
}

4
js/noticer.min.js vendored
View file

@ -1,4 +0,0 @@
/*! noticer - v0.1.1 - 2023-06-02
* https://git.zhirov.kz/alexander/noticer
* Copyright Alexander Zhirov; Licensed GPL-2.0 */
class Noticer{constructor(){this.div=$('<div id="noticer"></div>'),this.div.css({position:"absolute",top:"10px",right:"20px","z-index":"1000"}),$("body").append(this.div)}success(e,t=6e3){this.print(e,t,"#52b818","#bffdc0")}warning(e,t=6e3){this.print(e,t,"#b8ae18","#f8fdbf")}error(e,t=6e3){this.print(e,t,"#b96161","#fddede")}print=function(e,t,n,s){t<6e3&&(t=6e3);let a=function(e,t){let n,s,o=t;this.pause=function(){clearTimeout(n),o-=new Date-s},this.resume=function(){s=new Date,clearTimeout(n),n=setTimeout(e,o)},this.dead=function(){clearTimeout(n)},this.resume()},o=$(`<div class="notice">${e}</div>`);o.css({border:`1px solid ${n}`,"background-color":`${s}`,color:"#333",padding:"10px 30px","text-align":"center",display:"none",margin:"10px 0 0 0",width:"350px",opacity:"1",cursor:"pointer"}),o.hover(function(){$(this).css({opacity:"1"})},function(){$(this).css({opacity:"0.3"})}),this.div.append(o);let r=setTimeout(function(){o.fadeTo(1e3,.3)},2500);o.fadeIn(500).mouseenter(()=>{clearTimeout(r),i.pause()}).mouseleave(()=>{i.resume()}).click(()=>{i.dead(),o.fadeOut(0,function(){this.remove()})});let i=new a(()=>{o.fadeOut(500,function(){this.remove()})},t)}}

View file

@ -1,77 +1,27 @@
var numbers = [];
var sms = [];
var ussd = [];
$(document).ready(function () { $(document).ready(function () {
noticer = new Noticer; message = new Message;
let tabs = { // (new divNotFoundNumbers).push("Загрузка...");
0: () => { loadNumbers() },
1: () => { loadSMS() },
2: () => { loadUSSD() },
3: () => { loadServerInfo() }
};
let lists = {
0: () => { showListNumbers($("#accordion-numbers .ui-accordion-content-active")) },
1: () => { showListSMS($("#accordion-sms .ui-accordion-content-active")) },
2: () => { showListUSSD($("#accordion-ussd .ui-accordion-content-active")) },
3: () => {}
};
let groups = {
0: () => { generateListGroupNumbers($("#accordion-numbers .ui-accordion-content-active")) },
1: () => { generateListGroupSMS($("#accordion-sms .ui-accordion-content-active")) },
2: () => { generateListGroupUSSD($("#accordion-ussd .ui-accordion-content-active")) },
3: () => {}
};
$("button").button(); $("button").button();
$("#update-tab").button("option", "icon", "ui-icon-refresh"); $("#tabs").tabs();
$("#update-group").button("option", "icon", "ui-icon-arrowrefresh-1-s"); // $("#accordion-numbers").accordion();
$("#add-number").button("option", "icon", "ui-icon-plusthick");
$("#logout").button("option", "icon", "ui-icon-power");
$("#tabs").tabs({ // $(".addNumber").click(() => {
activate: function( event, ui ) { // numberAdd()
lists[$(this).tabs( "option", "active" )](); // });
$("#add-number").button( "option", "disabled", $(this).tabs( "option", "active" ) > 0 );
$("#update-group").button( "option", "disabled", $(this).tabs( "option", "active" ) > 2 );
}
});
$("#update-tab").click(() => {
tabs[$("#tabs").tabs( "option", "active" )]()
});
$("#update-group").click(() => {
groups[$("#tabs").tabs( "option", "active" )]()
});
$("#add-number").click(() => {
addNumber($("#accordion-numbers .ui-accordion-content-active"))
});
$("#search").on("input", function () {
lists[$("#tabs").tabs( "option", "active" )]()
}).keydown(function (e) {
e.key == "Escape" && ($(this).val(""), lists[$("#tabs").tabs( "option", "active" )]())
});
$("#logout").click(() => {
request('logout', 'json').then(data => {
data.error ? noticer.error(data.message) : (window.location.href = ".");
}).catch(error => {
noticer.error(error.message);
});
});
loadNumbers();
loadSMS();
loadUSSD();
loadServerInfo();
$("body").fadeTo(500, 1); $("body").fadeTo(500, 1);
// getData("Список номеров успешно загружен");
$(".search").on("input", function () {
// showNumbers(numbers.filter(e => e.id.includes($(this).val())))
}).keydown(function (e) {
// e.key == "Escape" && ($(this).val(""), showNumbers())
});
loadData();
}) })
async function request(query, type, queryData = {}) { async function request(query, type, queryData = {}) {
@ -101,591 +51,40 @@ function isJSON(str) {
} }
} }
function isNumeric(value) { function loadData() {
return /^-?\d+$/.test(value); request('listsgroups', 'json').then(data => {
} data.error ? message.error(data.message) : generateListsGroups(data);
function divNotFoundNumbers(text = '') {
let notFound = $('.notFoundNumbers');
let divTable = $('.body-rows');
let divNotFound = $(`<div class="notFoundNumbers">${text}</div>`);
divNotFound.css({
"color": "#333",
"text-align": "center",
"padding": "20px 0 20px 0"
});
this.push = function() {
divTable.append(divNotFound);
}
this.remove = function() {
notFound.remove();
}
}
function pEmpty(text) {
return $(`<p>${text}</p>`).css('text-align', 'center');
}
/************************************************************************************
Обработка таблицы с номерами телефонов
************************************************************************************/
function loadNumbers() {
request('listnumbergroups', 'text').then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else
generateListNumberGroups(data);
}).catch(error => { }).catch(error => {
noticer.error(error.message); message.error(error.message);
}); });
} }
function generateListNumberGroups(data) { function generateListsGroups(data) {
if (!$(data).children().length) { let numbers = $("#tabs-numbers");
$("#tabs-numbers").html(pEmpty('Номера телефонов отсутствуют')); let group = $('<div id="accordion-numbers"></div>');
return; $(data).each((i, j) => {
} group.append(`<h3>${j.comment}</h3><div class="group-content" data-group-name="${j.name}"></div>`);
});
$("#tabs-numbers").html(data); numbers.append(group);
$("#accordion-numbers").accordion({ $("#accordion-numbers").accordion({
heightStyle: "content", heightStyle: "content",
create: function( event, ui ) { create: function( event, ui ) {
generateListGroupNumbers(ui.panel); generateGroupNumbers(ui.panel);
}, },
beforeActivate: function( event, ui ) { beforeActivate: function( event, ui ) {
generateListGroupNumbers(ui.newPanel); generateGroupNumbers(ui.newPanel);
} }
}); });
} }
function generateListGroupNumbers(panel) { async function generateGroupNumbers(panel) {
if (!$("#accordion-numbers").children().length) { request('groupnumbers', 'text', { group: panel.data("group-name") }).then(data => {
noticer.warning("Номера телефонов отсутствуют");
$("#tabs-numbers").html(pEmpty('Номера телефонов отсутствуют'));
return;
}
request('listgroupnumbers', 'json', { group: panel.data("group-name") }).then(data => {
if (data.error)
noticer.error(data.message);
else {
numbers = data;
showListNumbers(panel);
}
}).catch(error => {
noticer.error(error.message);
});
}
function showListNumbers(panel, data = numbers.filter(e => e.number.includes($("#search").val()))) {
(new divNotFoundNumbers).remove();
let body = panel.find('.body').html('');
$(data).each((i, j) => {
let row = $(`<tr class="row" data-number="${j.number}"></tr>`);
row.append(`<td>${j.number}</td>`);
row.append(`<td>${j.list}</td>`);
row.append(`<td>${j.all_cc}</td>`);
row.append(`<td>${j.white_cc}</td>`);
row.append(`<td>${j.black_cc}</td>`);
row.append(`<td>${j.comment}</td>`);
body.append(row);
row.click(function() {
viewNumber(panel, $(this).data('number'));
});
});
if (!body.children().length)
(new divNotFoundNumbers('Нет номеров')).push();
}
function viewNumber(panel, number) {
request('viewnumber', 'text', {number: number}).then(data => {
if (isJSON(data) && JSON.parse(data).error) if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message); message.error(JSON.parse(data).message);
else { else {
showViewNumber(data, [ panel.html(data);
{
id: "btn-save",
text: "Сохранить",
icon: "ui-icon-check",
click: function() {
actionNumber(panel, $(this), 'updatenumber');
}
},
{
id: "btn-delete",
text: "Удалить",
icon: "ui-icon-trash",
click: function() {
delNumber(panel, $(this));
}
}
], `Редактирование номера ${number}`);
} }
}).catch(error => { }).catch(error => {
noticer.error(error.message); message.error(error.message);
});
}
function showViewNumber(data, actionButton, title) {
let form = $(data);
form.appendTo('body').dialog({
title: title,
height: 'auto',
width: 'auto',
resizable: false,
modal: true,
show: { effect: "fade", duration: 500 },
close: function(event, ui) {
$(this).dialog('destroy').remove()
},
buttons: [
...actionButton,
{
text: "Отмена",
icon: "ui-icon-cancel",
click: function() {
$(this).dialog("close");
}
}
]
});
$('#number-group, #number-list').selectmenu({
width: 200
});
}
function actionNumber(panel, currentWindow, query) {
let pattern_number = /^\+7\d{10}$/g;
let number = $('#number-number').val();
let group = $('#number-group').val();
let list = $('#number-list').val();
let all_cc = $('#number-all-cc').val();
let white_cc = $('#number-white-cc').val();
let black_cc = $('#number-black-cc').val();
let comment = $('#number-comment').val();
let error = false;
if (number.match(pattern_number) === null) { noticer.warning("Номер не соответствует формату +7XXXXXXXXXX"); error = true; }
if (!isNumeric(all_cc)) { noticer.warning("Общее количество звонков должно быть числом"); error = true; }
if (all_cc < 0) { noticer.warning("Общее количество звонков не может быть отрицательным"); error = true; }
if (!isNumeric(white_cc)) { noticer.warning("Белое количество звонков должно быть числом"); error = true; }
if (white_cc < 0) { noticer.warning("Белое количество звонков не может быть отрицательным"); error = true; }
if (!isNumeric(black_cc)) { noticer.warning("Черное количество звонков должно быть числом"); error = true; }
if (black_cc < 0) { noticer.warning("Черное количество звонков не может быть отрицательным"); error = true; }
if (error) return;
request(query, 'json', {
number: number,
group: group,
list: list,
all_cc: parseInt(all_cc),
white_cc: parseInt(white_cc),
black_cc: parseInt(black_cc),
comment: comment
}).then(data => {
if (data.error)
noticer.error(data.message);
else
{
query == 'write' ? noticer.success(`Номер ${number} был добавлен`) : noticer.success(`Номер ${number} был обновлен`);
generateListGroupNumbers(panel);
currentWindow.dialog("close");
}
}).catch(error => {
noticer.error(error.message);
});
}
function addNumber(panel) {
request('addnumber', 'text', { group: panel.data("group-name") }).then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else {
showViewNumber(data, [{
id: "btnSave",
text: "Добавить",
icon: "ui-icon-check",
click: function() {
actionNumber(panel, $(this), 'writenumber');
}
}], "Добавление нового номера");
}
}).catch(error => {
noticer.error(error.message);
});
}
function delNumber(panel, currentWindow) {
let number = $('#number-number').val();
let error = false;
if (!number.length) { noticer.warning('Номер не может быть пуст'); error = true; }
if (error) return;
request('delnumber', 'json', {
number: number
}).then(data => {
if (data.error)
noticer.error(data.message);
else {
noticer.success(`Номер ${number} был удален`);
generateListGroupNumbers(panel);
currentWindow.dialog("close");
}
}).catch(error => {
noticer.error(error.message);
});
}
/************************************************************************************
Обработка таблицы с SMS
************************************************************************************/
function loadSMS() {
request('listsmsgroups', 'text').then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else
generateListSMSGroups(data);
}).catch(error => {
noticer.error(error.message);
});
}
function generateListSMSGroups(data) {
if (!$(data).children().length) {
$("#tabs-sms").html(pEmpty('SMS отсутствуют'));
return;
}
$("#tabs-sms").html(data);
$("#accordion-sms").accordion({
heightStyle: "content",
create: function( event, ui ) {
generateListGroupSMS(ui.panel);
},
beforeActivate: function( event, ui ) {
generateListGroupSMS(ui.newPanel);
}
})
}
function generateListGroupSMS(panel) {
if (!$("#accordion-sms").children().length) {
noticer.warning("SMS отсутствуют");
$("#tabs-sms").html(pEmpty('SMS отсутствуют'));
return;
}
request('listgroupsms', 'json', { to: panel.data("to") }).then(data => {
if (data.error)
noticer.error(data.message);
else {
sms = data;
showListSMS(panel);
}
}).catch(error => {
noticer.error(error.message);
});
}
function showListSMS(panel, data = sms.filter(e => e.from.includes($("#search").val()))) {
(new divNotFoundNumbers).remove();
let body = panel.find('.body').html('');
$(data).each((i, j) => {
let row = $(`<tr class="row" data-sms-id="${j.id}"></tr>`);
row.append(`<td class="sms-content-width">${j.date}</td>`);
row.append(`<td class="sms-content-width">${j.from}</td>`);
row.append(`<td>${j.text}</td>`);
body.append(row);
row.click(function() {
viewSMS(panel, $(this).data('sms-id'), j.from);
});
});
if (!body.children().length)
(new divNotFoundNumbers('Нет SMS')).push();
}
function viewSMS(panel, id, number) {
request('viewsms', 'text', {id: id}).then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else {
showViewSMS(data, [
{
id: "btn-delete",
text: "Удалить",
icon: "ui-icon-trash",
click: function() {
delSMS(panel, $(this));
}
}
], `SMS от ${number}`);
}
}).catch(error => {
noticer.error(error.message);
});
}
function showViewSMS(data, actionButton, title) {
let form = $(data);
form.appendTo('body').dialog({
title: title,
height: 'auto',
width: 'auto',
resizable: false,
modal: true,
show: { effect: "fade", duration: 500 },
close: function(event, ui) {
$(this).dialog('destroy').remove()
},
buttons: [
...actionButton,
{
text: "Отмена",
icon: "ui-icon-cancel",
click: function() {
$(this).dialog("close");
}
}
]
});
}
function delSMS(panel, currentWindow) {
let id = $('#sms-content').data('id');
let from = $('#sms-content').data('from');
request('delsms', 'json', {
id: id
}).then(data => {
if (data.error)
noticer.error(data.message);
else {
noticer.success(`SMS от ${from} было удалено`);
generateListGroupSMS(panel);
currentWindow.dialog("close");
}
}).catch(error => {
noticer.error(error.message);
});
}
/************************************************************************************
Обработка таблицы с USSD
************************************************************************************/
function loadUSSD() {
request('listussdgroups', 'text').then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else
generateListUSSDGroups(data);
}).catch(error => {
noticer.error(error.message);
});
}
function generateListUSSDGroups(data) {
if (!$(data).children().length) {
$("#tabs-ussd").html(pEmpty('USSD отсутствуют'));
return;
}
$("#tabs-ussd").html(data);
$("#accordion-ussd").accordion({
heightStyle: "content",
create: function( event, ui ) {
generateListGroupUSSD(ui.panel);
},
beforeActivate: function( event, ui ) {
generateListGroupUSSD(ui.newPanel);
}
})
}
function generateListGroupUSSD(panel) {
if (!$("#accordion-ussd").children().length) {
noticer.warning("USSD отсутствуют");
$("#tabs-ussd").html(pEmpty('USSD отсутствуют'));
return;
}
request('listgroupussd', 'json', { to: panel.data("to") }).then(data => {
if (data.error)
noticer.error(data.message);
else {
ussd = data;
showListUSSD(panel);
}
}).catch(error => {
noticer.error(error.message);
});
}
function showListUSSD(panel, data = ussd) {
(new divNotFoundNumbers).remove();
let body = panel.find('.body').html('');
$(data).each((i, j) => {
let row = $(`<tr class="row" data-ussd-id="${j.id}"></tr>`);
row.append(`<td class="ussd-content-width">${j.date}</td>`);
row.append(`<td class="ussd-content-width-type">${j.type_comment}</td>`);
row.append(`<td>${j.text}</td>`);
body.append(row);
row.click(function() {
viewUSSD(panel, $(this).data('ussd-id'), j.type_comment);
});
});
if (!body.children().length)
(new divNotFoundNumbers('Нет USSD')).push();
}
function viewUSSD(panel, id, type) {
request('viewussd', 'text', {id: id}).then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else {
showViewUSSD(data, [
{
id: "btn-delete",
text: "Удалить",
icon: "ui-icon-trash",
click: function() {
delUSSD(panel, $(this));
}
}
], `USSD: ${type}`);
}
}).catch(error => {
noticer.error(error.message);
});
}
function showViewUSSD(data, actionButton, title) {
let form = $(data);
form.appendTo('body').dialog({
title: title,
height: 'auto',
width: 'auto',
resizable: false,
modal: true,
show: { effect: "fade", duration: 500 },
close: function(event, ui) {
$(this).dialog('destroy').remove()
},
buttons: [
...actionButton,
{
text: "Отмена",
icon: "ui-icon-cancel",
click: function() {
$(this).dialog("close");
}
}
]
});
}
function delUSSD(panel, currentWindow) {
let id = $('#ussd-content').data('id');
request('delussd', 'json', {
id: id
}).then(data => {
if (data.error)
noticer.error(data.message);
else {
noticer.success(`USSD было удалено`);
generateListGroupUSSD(panel);
currentWindow.dialog("close");
}
}).catch(error => {
noticer.error(error.message);
});
}
/************************************************************************************
Обработка таблицы информации о сервере
************************************************************************************/
function loadServerInfo() {
request('serverinfo', 'text').then(data => {
if (isJSON(data) && JSON.parse(data).error)
noticer.error(JSON.parse(data).message);
else
showServerInfo(data);
}).catch(error => {
noticer.error(error.message);
});
}
function showServerInfo(data) {
$("#tabs-server").html(data);
$("#server-external-number-on").checkboxradio();
$("#server-transparent-mode").checkboxradio();
$("#server-button").button({ icon: "ui-icon-disk", disabled: true });
$(".server-input").on("change paste cut keydown", () => {
if ($("#server-button").button( "option", "disabled")) {
noticer.warning('Некоторые параметры сервера были изменены');
$("#server-button").button( "option", "disabled", false);
}
});
$("#server-button").click(() => {
writeServerInfo();
});
}
function writeServerInfo() {
let pattern_number = /^\+7\d{10}$/g;
let internal_number = $("#server-internal-number").val();
let external_number = $("#server-external-number").val();
let external_number_on = $("#server-external-number-on").is(":checked");
let transparent_mode = $("#server-transparent-mode").is(":checked");
let error = false;
if (external_number.match(pattern_number) === null) { noticer.warning("Внешний номер не соответствует формату +7XXXXXXXXXX"); error = true; }
if (error) return;
request('writeserverinfo', 'json', {
internal_number: internal_number,
external_number: external_number,
external_number_on: external_number_on,
transparent_mode: transparent_mode
}).then(data => {
data.error ?
noticer.error(data.message) :
$("#server-button").button( "option", "disabled", true) && noticer.success("Параметры сервера были сохранены")
}).catch(error => {
noticer.error(error.message);
}); });
} }

View file

@ -1,18 +0,0 @@
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
div.page {
display: flex;
align-items: center;
}
.pic {
background-image: url("404.png");
min-width: 512px;
min-height: 512px;
background-size: contain;
}

View file

@ -1,62 +0,0 @@
@font-face {
font-family: Scada;
src: url(Scada-Regular.ttf);
}
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin-top: -5%;
color: #333;
opacity: 0;
font-family: Scada;
}
div.form {
display: flex;
flex-direction: column;
align-items: center;
}
.div-button {
display: flex;
justify-content: center;
margin-top: 10px;
}
input {
text-align: center;
color: #333;
border: 1px solid#c5c5c5;
height: 30px;
}
input:hover {
border: 1px solid #999;
box-shadow: 1px 1px 10px 1px #ccc;
}
.input-focus:focus {
outline: none;
box-shadow: 1px 1px 10px 1px #666;
border: 1px solid #555;
}
.logo {
background-image: url("favicon.png");
min-width: 128px;
min-height: 128px;
background-size: contain;
margin-bottom: 20px;
}
.title {
margin: 30px;
}
.label {
text-align: right;
padding-right: 10px;
}

View file

@ -18,16 +18,16 @@ body {
div.div-header { div.div-header {
display: flex; display: flex;
align-items: center; align-items: center;
width: 70%; width: 60%;
justify-content: center; justify-content: center;
margin-top: 30px margin-top: 30px
} }
/* HEADER */ /* HEADER */
div.main-button { /* div.div-add {
margin-right: 20px margin-right: 20px
} } */
div.div-search { div.div-search {
display: flex; display: flex;
@ -49,41 +49,36 @@ input {
border: 1px solid#c5c5c5; border: 1px solid#c5c5c5;
} }
.input-focus:hover:not([disabled]) { .input-focus:hover {
border: 1px solid #999; border: 1px solid #ccc
box-shadow: 1px 1px 10px 1px #ccc;
} }
.input-focus::placeholder { .input-focus::placeholder {
color: #333 color: #333
} }
.input-focus:focus:not([disabled]) { .input-focus:focus {
outline: none; outline: none;
box-shadow: 1px 1px 10px 1px #666; box-shadow: 1px 1px 10px 1px #007fff;
border: 1px solid #555; border: 1px solid #003eff;
} }
/* BODY */ /* BODY */
.content { .content {
width: 70%; width: 60%;
margin-top: 20px margin-top: 20px
} }
.table-content { .content table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
table-layout: fixed; table-layout: fixed;
} }
div.group-content {
height: 55vh;
}
div.body-rows { div.body-rows {
height: calc(100% - 55px); max-height: 55vh;
overflow-x: auto; overflow-x: auto;
border: 1px solid #c5c5c5; border: 1px solid #c5c5c5;
border-top: 0; border-top: 0;
@ -101,112 +96,12 @@ td {
text-align: center; text-align: center;
} }
.body-rows td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.body-rows tbody tr:nth-child(even){ .body-rows tbody tr:nth-child(even){
background: #fff; background: #fff;
} }
tr.row {
border-top: 1px solid #fff;
}
tr.row:hover, tr.row:nth-child(even):hover { tr.row:hover, tr.row:nth-child(even):hover {
background-color: #f6f6f6; background-color: #c5c5c5;
color: #003eff; color: #fff;
cursor: pointer; cursor: pointer;
border-bottom: 1px solid #c5c5c5;
border-top: 1px solid #c5c5c5;
}
/* EDIT NUMBER */
.number-label, .sms-label, .ussd-label, .server-label {
color: #333;
text-align: right;
}
.number-value, .server-value {
height: 30px;
}
.server-value {
text-align: left;
padding: 2px 0 2px 10px;
}
.server-input {
height: 100%;
}
.server-button {
margin-top: 30px;
}
.sms-label-text, .ussd-label-text {
vertical-align:top
}
.sms-value, .ussd-value {
width: 300px;
text-align: left;
}
.number-input-main {
height: 25px;
width: 194px;
margin: 0 10px 0 10px;
}
.number-input-cc {
height: 25px;
width: 50px;
margin: 0 10px 0 10px;
}
.div-advanced {
display: flex;
flex-direction: column;
}
.comment {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.comment-name {
padding: 15px 0 5px 0;
}
.comment-content, .sms-content, .ussd-content {
width: 100%;
height: 100%;
}
#number-comment, #sms-content, #ussd-content {
width: calc(100% - 6px);
resize: none;
}
#sms-content, #ussd-content {
height: 150px;
outline: none;
border: 1px solid#c5c5c5;
color: #333;
width: 400px;
padding: 5px 10px 5px 10px;
}
th.sms-content-width, td.sms-content-width, th.ussd-content-width, td.ussd-content-width {
width: 155px;
}
th.ussd-content-width-type, td.ussd-content-width-type {
width: 240px;
} }

View file

@ -1,18 +1,14 @@
[web-host] [web-host]
title => "Управление диалпланом" title => "Управление диалпланом"
addresses => 127.0.0.1 addresses => 127.0.0.1
http => 80 http => 8080
https => 443 https => 443
cert => certs/test.local.crt cert => certs/test.local.crt
key => certs/test.local.key key => certs/test.local.key
data => ./ ; Путь к каталогу, в котором расположены каталоги public, jq, js, images data => ./ ; Путь к каталогу, в котором расположены каталоги public, js, images
loglevel => 0 ; 0 - debug, 1 - crit, 2 - err, 3 - warn, 4 - notice, 5 - info, 6 - alert loglevel => 0 ; 0 - debug, 1 - crit, 2 - err, 3 - warn, 4 - notice, 5 - info, 6 - alert
logoutput => 1, 4 ; 1 - syslog, 2 - stout, 4 - file => example: 1,2 or 1,2,4 logoutput => 1, 4 ; 1 - syslog, 2 - stout, 4 - file => example: 1,2 or 1,2,4
logfile => /var/log/daster.log ; if log-output set with 4 logfile => /var/log/jaster.log ; if log-output set with 4
[auth]
login =>
password =>
[daster-db] [daster-db]
host => 127.0.0.1 host => 127.0.0.1

View file

@ -15,29 +15,19 @@ import std.array;
import verinfo; import verinfo;
import pgdb; import pgdb;
import structures; import structures;
import response;
import requests.numbers; import requests.listsgroups;
import requests.sms; import requests.groupnumbers;
import requests.ussd;
import requests.server;
import requests.authorization;
static ServerInfo serverInfo; static ServerInfo serverInfo;
static AuthData serverAuthData;
private void showVersion() { private void showVersion() {
writefln("daster версия %s, собрано %s", getDasterVersion(), __DATE__); writefln("daster версия %s, собрано %s", getDasterVersion(), __DATE__);
} }
void page404(HTTPServerRequest req, HTTPServerResponse res, HTTPServerErrorInfo error) {
res.render!("404.dt", req, error);
}
int main(string[] args) { int main(string[] args) {
log.level(log.INFORMATION) log.level(log.INFORMATION);
.output(log.SYSLOG) log.output(log.SYSLOG);
.color(true);
bool flagVersion; bool flagVersion;
string flagSettings; string flagSettings;
@ -83,17 +73,15 @@ int main(string[] args) {
} }
rc.read(flagSettings); rc.read(flagSettings);
rcAsteriskDB();
auto webHost = rcWebHost(); auto webHost = rcWebHost();
serverInfo = ServerInfo(webHost.title); serverInfo = ServerInfo(webHost.title);
if (webHost.loglevel != -1) log.level(webHost.loglevel); if (webHost.loglevel != -1) log.level(webHost.loglevel);
if (webHost.logoutput) log.output(webHost.logoutput); if (webHost.logoutput) log.output(webHost.logoutput);
if (webHost.logfile.length) log.file(webHost.logfile); if (webHost.logfile.length) log.file(webHost.logfile);
rcAsteriskDB();
rcAuth();
auto router = new URLRouter; auto router = new URLRouter;
router.post("/", &postReq); router.post("/", &postReq);
router.get("/", &getReq); router.get("/", &getReq);
@ -108,14 +96,12 @@ int main(string[] args) {
auto settingsHTTPS = new HTTPServerSettings; auto settingsHTTPS = new HTTPServerSettings;
if (webHost.http) { if (webHost.http) {
settingsHTTP.errorPageHandler = toDelegate(&page404);
settingsHTTP.sessionStore = memorySessionStore; settingsHTTP.sessionStore = memorySessionStore;
settingsHTTP.port = webHost.http; settingsHTTP.port = webHost.http;
settingsHTTP.bindAddresses = ["::1"] ~ webHost.addresses; settingsHTTP.bindAddresses = ["::1"] ~ webHost.addresses;
} }
if (webHost.https) { if (webHost.https) {
settingsHTTPS.errorPageHandler = toDelegate(&page404);
settingsHTTPS.sessionStore = memorySessionStore; settingsHTTPS.sessionStore = memorySessionStore;
settingsHTTPS.port = webHost.https; settingsHTTPS.port = webHost.https;
settingsHTTPS.bindAddresses = ["::1"] ~ webHost.addresses; settingsHTTPS.bindAddresses = ["::1"] ~ webHost.addresses;
@ -149,15 +135,16 @@ void startWebServer(WebHost wh, HTTPServerSettings http, HTTPServerSettings http
} }
void getReq(HTTPServerRequest req, HTTPServerResponse res) { void getReq(HTTPServerRequest req, HTTPServerResponse res) {
if (req.session) { // if (req.session) {
auto user = req.session.get!UserData("user"); // auto user = req.session.get!UserData("userData");
if (user.login) { // if (user.loggedIn) {
renderMainPage(req, res); // renderMainPage(req, res);
return; // return;
} // }
} // }
render!("authorization.dt", serverInfo)(res); // render!("index.dt", serverInfo)(res);
renderMainPage(req, res);
} }
void renderMainPage(HTTPServerRequest req, HTTPServerResponse res) { void renderMainPage(HTTPServerRequest req, HTTPServerResponse res) {
@ -172,74 +159,45 @@ void postReq(HTTPServerRequest req, HTTPServerResponse res) {
if (query.empty) return; if (query.empty) return;
if (query != "login" && !checkAuth(req)) { // if (query != "authorization" && !checkAuth(req)) {
res.send( // res.send(
true, // true,
"Сессия не существует. Перезагрузите страницу" // "Сессия не существует. Перезагрузите страницу"
); // );
return; // return;
} // }
log.d("%s: json request %s".format(req.clientAddress.toAddressString(), jsr.to!string));
switch (query) { switch (query) {
case "login": case "listsgroups":
login(req, res, serverAuthData); listsgroups(req, res);
break; break;
case "logout": case "groupnumbers":
logout(req, res); groupnumbers(req, res);
break;
case "listnumbergroups":
getListNumberGroups(req, res);
break;
case "listgroupnumbers":
getListGroupNumbers(req, res);
break;
case "viewnumber":
getViewNumber(req, res);
break;
case "addnumber":
getAddNumber(req, res);
break;
case "writenumber":
sendWriteNumber(req, res);
break;
case "updatenumber":
sendUpdateNumber(req, res);
break;
case "delnumber":
sendDelNumber(req, res);
break;
case "listsmsgroups":
getListSMSGroups(req, res);
break;
case "listgroupsms":
getListGroupSMS(req, res);
break;
case "viewsms":
getViewSMS(req, res);
break;
case "delsms":
sendDelSMS(req, res);
break;
case "listussdgroups":
getListUSSDGroups(req, res);
break;
case "listgroupussd":
getListGroupUSSD(req, res);
break;
case "viewussd":
getViewUSSD(req, res);
break;
case "delussd":
sendDelUSSD(req, res);
break;
case "serverinfo":
getServerInfo(req, res);
break;
case "writeserverinfo":
sendWriteServerInfo(req, res);
break; break;
// case "authorization":
// authorization(req, res);
// break;
// case "logout":
// logout(req, res);
// break;
// case "numbers":
// numbers(req, res);
// break;
// case "add":
// addNumber(req, res);
// break;
// case "write":
// writeNumber(req, res);
// break;
// case "edit":
// editNumber(req, res);
// break;
// case "update":
// updateNumber(req, res);
// break;
// case "remove":
// removeNumber(req, res);
// break;
default: default:
res.redirect("/"); res.redirect("/");
} }
@ -284,7 +242,7 @@ void rcAsteriskDB() {
" password=" ~ asteriskDB["password"] " password=" ~ asteriskDB["password"]
); );
} catch (Exception e) { } catch (Exception e) {
log.c(e.msg); log.c(e);
exit(1); exit(1);
} }
} }
@ -445,24 +403,3 @@ WebHost rcWebHost() {
return wh; return wh;
} }
void rcAuth() {
ConfigSection auth;
try {
auth = rc[]["auth"];
} catch (Exception e) {
log.c("В конфигурационном файле не верны настройки auth");
exit(1);
}
if (auth["login"].empty)
log.w("Логин не был установлен - auth.login");
else
serverAuthData.login = auth["login"];
if (auth["password"].empty)
log.w("Пароль не был установлен - auth.password");
else
serverAuthData.password = auth["password"];
}

67
source/data.d Normal file
View file

@ -0,0 +1,67 @@
module data;
import pgdb;
import singlog;
import structures;
import std.conv;
GroupDB[] getListGroups() {
GroupDB[] groups;
try {
auto queryResult = pgsql.sql(
"select distinct
n.da_group,
g.da_comment
from da_numbers n
left join da_groups g ON g.da_name = n.da_group"
);
foreach (row; queryResult) {
GroupDB data;
data.name = row["da_group"];
data.comment = row["da_comment"];
groups ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return groups;
}
NumberDB[] getListNumbers(string group) {
NumberDB[] numbers;
try {
auto queryResult = pgsql.sql(
"select
dan.da_number,
dal.da_comment da_list,
dan.da_all_cc,
dan.da_white_cc,
dan.da_black_cc,
dan.da_comment
from da_numbers dan
left join da_lists dal on dal.da_name = dan.da_list
where da_group = ?",
group
);
foreach (row; queryResult) {
NumberDB data;
data.number = row["da_number"];
data.list = row["da_list"];
data.all_cc = row["da_all_cc"].to!int;
data.white_cc = row["da_white_cc"].to!int;
data.black_cc = row["da_black_cc"].to!int;
data.comment = row["da_comment"];
numbers ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return numbers;
}

View file

@ -1,47 +0,0 @@
module requests.authorization;
import vibe.vibe;
import response;
import structures;
import singlog;
void login(HTTPServerRequest req, HTTPServerResponse res, AuthData serverAuthData) {
auto userAuthData = deserializeJson!AuthData(req.json);
if (!(serverAuthData.login == userAuthData.login &&
serverAuthData.password == userAuthData.password)) {
log.i(req.clientAddress.toAddressString() ~ ": Данные авторизации не верны");
res.send(
true,
"Данные авторизации не верны"
);
return;
}
auto user = UserData(true);
req.session = res.startSession();
req.session.set!UserData("user", user);
log.i(req.clientAddress.toAddressString() ~ ": Вход в систему");
res.send();
}
void logout(HTTPServerRequest req, HTTPServerResponse res) {
req.session.set!UserData("user", UserData.init);
res.terminateSession();
log.i(req.clientAddress.toAddressString() ~ ": Выход из системы");
res.send();
}
bool checkAuth(HTTPServerRequest req) {
if (req.session)
return req.session.get!UserData("user").login;
log.d(req.clientAddress.toAddressString() ~ ": Отсутствует авторизация");
return false;
}

View file

@ -0,0 +1,12 @@
module requests.groupnumbers;
import vibe.vibe;
import response;
import data;
import singlog;
void groupnumbers(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
auto listNumbers = getListNumbers(jsr["group"].get!string);
render!("group-numbers-list.dt", listNumbers)(res);
}

View file

@ -0,0 +1,11 @@
module requests.listsgroups;
import vibe.vibe;
import response;
import data;
void listsgroups(HTTPServerRequest req, HTTPServerResponse res) {
// auto jsr = req.json;
res.writeJsonBody(getListGroups().serializeToJson());
}

View file

@ -1,113 +0,0 @@
module requests.numbers;
import vibe.vibe;
import response;
import structures;
import sql;
import singlog;
import std.regex;
// Получить список всех групп номеров
void getListNumberGroups(HTTPServerRequest req, HTTPServerResponse res) {
auto listGroups = sqlGetListGroups();
render!("list_number_groups.dt", listGroups)(res);
}
// Получить список номеров конкретной группы
void getListGroupNumbers(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
res.writeJsonBody(sqlGetListNumbers(jsr["group"].get!string).serializeToJson());
}
// Добавление номера телефона
void getAddNumber(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
bool edit = false;
NumberDB dataNumber;
dataNumber.group = jsr["group"].get!string;
auto groups = sqlGetGroups();
auto lists = sqlGetLists();
render!("number.dt", edit, dataNumber, groups, lists)(res);
}
// Просмотр номера телефона
void getViewNumber(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
bool edit = true;
auto dataNumber = sqlGetDataNumber(jsr["number"].get!string);
auto groups = sqlGetGroups();
auto lists = sqlGetLists();
render!("number.dt", edit, dataNumber, groups, lists)(res);
}
// Обновить номер телефона
void sendUpdateNumber(HTTPServerRequest req, HTTPServerResponse res) {
NumberDB number = deserializeJson!NumberDB(req.json);
if (!checkNumber(number, res))
return;
if (!sqlUpdateNumber(number)) {
res.send(true, "Не удалось обновить номер");
return;
}
res.send();
}
// Записать номер телефона
void sendWriteNumber(HTTPServerRequest req, HTTPServerResponse res) {
NumberDB number = deserializeJson!NumberDB(req.json);
if (!checkNumber(number, res))
return;
if (!sqlInsertNumber(number)) {
res.send(true, "Не удалось записать номер");
return;
}
res.send();
}
// Удалить номера телефона
void sendDelNumber(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
string number = jsr["number"].get!string;
if (!number.length) {
res.send(true, "Номер не может быть пуст");
return;
}
if (!sqlDeleteNumber(number)) {
res.send(true, "Не удалось удалить номер");
return;
}
res.send();
}
// Проверка номера перед изменением
bool checkNumber(NumberDB number, HTTPServerResponse res) {
if (!number.number.matchFirst(regex(r"^\+7\d{10}$", "g"))) {
res.send(true, "Номер не соответствует формату +7XXXXXXXXXX");
return false;
}
if (number.all_cc < 0) {
res.send(true, "Общее количество звонков не может быть отрицательным");
return false;
}
if (number.white_cc < 0) {
res.send(true, "Белое количество звонков не может быть отрицательным");
return false;
}
if (number.black_cc < 0) {
res.send(true, "Черное количество звонков не может быть отрицательным");
return false;
}
return true;
}

View file

@ -1,30 +0,0 @@
module requests.server;
import vibe.vibe;
import response;
import structures;
import sql;
import singlog;
import std.regex;
// Получить информацию о сервере
void getServerInfo(HTTPServerRequest req, HTTPServerResponse res) {
auto dataServer = sqlGetServerInfo();
render!("server.dt", dataServer)(res);
}
void sendWriteServerInfo(HTTPServerRequest req, HTTPServerResponse res) {
ServerDB server = deserializeJson!ServerDB(req.json);
if (!server.external_number.matchFirst(regex(r"^\+7\d{10}$", "g"))) {
res.send(true, "Внешний номер не соответствует формату +7XXXXXXXXXX");
return;
}
if (!sqlUpdateServerInfo(server)) {
res.send(true, "Не удалось записать параметры сервера");
return;
}
res.send();
}

View file

@ -1,39 +0,0 @@
module requests.sms;
import vibe.vibe;
import response;
import structures;
import sql;
import singlog;
// Получить список всех групп SMS
void getListSMSGroups(HTTPServerRequest req, HTTPServerResponse res) {
auto numbers = sqlGetSMSNumbers();
render!("list_sms_groups.dt", numbers)(res);
}
// Получить список SMS конкретной группы
void getListGroupSMS(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
res.writeJsonBody(sqlGetListSMS(jsr["to"].get!string).serializeToJson());
}
// Просмотр SMS
void getViewSMS(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
auto dataSMS = sqlGetSMS(jsr["id"].to!int);
render!("sms.dt", dataSMS)(res);
}
// Удалить SMS
void sendDelSMS(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
int idsms = jsr["id"].get!int;
if (!sqlDeleteSMS(idsms)) {
res.send(true, "Не удалось удалить SMS");
return;
}
res.send();
}

View file

@ -1,39 +0,0 @@
module requests.ussd;
import vibe.vibe;
import response;
import structures;
import sql;
import singlog;
// Получить список всех групп USSD
void getListUSSDGroups(HTTPServerRequest req, HTTPServerResponse res) {
auto numbers = sqlGetUSSDNumbers();
render!("list_ussd_groups.dt", numbers)(res);
}
// Получить список USSD конкретной группы
void getListGroupUSSD(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
res.writeJsonBody(sqlGetListUSSD(jsr["to"].get!string).serializeToJson());
}
// Просмотр USSD
void getViewUSSD(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
auto dataUSSD = sqlGetUSSD(jsr["id"].to!int);
render!("ussd.dt", dataUSSD)(res);
}
// Удалить USSD
void sendDelUSSD(HTTPServerRequest req, HTTPServerResponse res) {
auto jsr = req.json;
int idussd = jsr["id"].get!int;
if (!sqlDeleteUSSD(idussd)) {
res.send(true, "Не удалось удалить USSD");
return;
}
res.send();
}

View file

@ -1,454 +0,0 @@
module sql;
import pgdb;
import singlog;
import structures;
import std.conv;
/***********************************************************
Запросы для таблицы номеров телефонов
***********************************************************/
GroupDB[] sqlGetListGroups() {
GroupDB[] groups;
try {
auto queryResult = pgsql.sql(
"select distinct
dan.da_group,
dag.da_comment
from da_numbers dan
left join da_groups dag ON dag.da_name = dan.da_group"
);
foreach (row; queryResult) {
GroupDB data;
data.name = row["da_group"];
data.comment = row["da_comment"];
groups ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return groups;
}
NumberDB[] sqlGetListNumbers(string group) {
NumberDB[] numbers;
try {
auto queryResult = pgsql.sql(
"select
dan.da_number,
dal.da_comment da_list,
dan.da_all_cc,
dan.da_white_cc,
dan.da_black_cc,
dan.da_comment
from da_numbers dan
left join da_lists dal on dal.da_name = dan.da_list
where dan.da_group = ?
order by dan.da_number",
group
);
foreach (row; queryResult) {
NumberDB data;
data.number = row["da_number"];
data.list = row["da_list"];
data.all_cc = row["da_all_cc"].to!int;
data.white_cc = row["da_white_cc"].to!int;
data.black_cc = row["da_black_cc"].to!int;
data.comment = row["da_comment"];
numbers ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return numbers;
}
NumberDB sqlGetDataNumber(string number) {
NumberDB data;
try {
auto queryResult = pgsql.sql(
"select
da_number,
da_group,
da_list,
da_all_cc,
da_white_cc,
da_black_cc,
da_comment
from da_numbers
where da_number = ?",
number
);
foreach (row; queryResult) {
data.number = row["da_number"];
data.group = row["da_group"];
data.list = row["da_list"];
data.all_cc = row["da_all_cc"].to!int;
data.white_cc = row["da_white_cc"].to!int;
data.black_cc = row["da_black_cc"].to!int;
data.comment = row["da_comment"];
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return data;
}
GroupDB[] sqlGetGroups() {
GroupDB[] groups;
try {
auto queryResult = pgsql.sql(
"select da_name, da_comment from da_groups"
);
foreach (row; queryResult) {
GroupDB data;
data.name = row["da_name"];
data.comment = row["da_comment"];
groups ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return groups;
}
ListDB[] sqlGetLists() {
ListDB[] lists;
try {
auto queryResult = pgsql.sql(
"select da_name, da_comment from da_lists"
);
foreach (row; queryResult) {
ListDB data;
data.name = row["da_name"];
data.comment = row["da_comment"];
lists ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return lists;
}
bool sqlUpdateNumber(NumberDB number) {
try {
pgsql.sql(
"update da_numbers set
da_group = ?,
da_list = ?,
da_all_cc = ?,
da_white_cc = ?,
da_black_cc = ?,
da_comment = ?
where da_number = ?",
number.group,
number.list,
number.all_cc,
number.white_cc,
number.black_cc,
number.comment,
number.number
);
} catch (Exception e) {
log.e("Ошибка обновления номера в БД. " ~ e.msg);
return false;
}
return true;
}
bool sqlDeleteNumber(string number) {
try {
pgsql.sql(
"delete from da_numbers where da_number = ?", number
);
} catch (Exception e) {
log.e("Ошибка удаления номера в БД. " ~ e.msg);
return false;
}
return true;
}
bool sqlInsertNumber(NumberDB number) {
try {
pgsql.sql(
"insert into da_numbers
(da_number, da_group, da_list, da_all_cc, da_white_cc, da_black_cc, da_comment)
values
(?, ?, ?, ?, ?, ?, ?)",
number.number,
number.group,
number.list,
number.all_cc,
number.white_cc,
number.black_cc,
number.comment
);
} catch (Exception e) {
log.error("Ошибка добавления номера телефона в БД. " ~ e.msg);
return false;
}
return true;
}
/***********************************************************
Запросы для таблицы SMS
***********************************************************/
SMSDB[] sqlGetSMSNumbers() {
SMSDB[] numbers;
try {
auto queryResult = pgsql.sql(
"select distinct da_to from da_sms"
);
foreach (row; queryResult) {
SMSDB data;
data.to = row["da_to"];
numbers ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return numbers;
}
SMSDB[] sqlGetListSMS(string to) {
SMSDB[] sms;
try {
auto queryResult = pgsql.sql(
"select
da_id,
to_char(da_date, 'YYYY.MM.DD HH24:MI:SS') da_date,
da_to,
da_from,
da_text
from da_sms
where da_to = ?
order by da_date desc",
to
);
foreach (row; queryResult) {
SMSDB data;
data.id = row["da_id"].to!int;
data.date = row["da_date"];
data.to = row["da_to"];
data.from = row["da_from"];
data.text = row["da_text"];
sms ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return sms;
}
SMSDB sqlGetSMS(int idsms) {
SMSDB data;
try {
auto queryResult = pgsql.sql(
"select
da_id,
to_char(da_date, 'YYYY.MM.DD HH24:MI:SS') da_date,
da_to,
da_from,
da_text
from da_sms
where da_id = ?",
idsms
);
foreach (row; queryResult) {
data.id = row["da_id"].to!int;
data.date = row["da_date"];
data.to = row["da_to"];
data.from = row["da_from"];
data.text = row["da_text"];
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return data;
}
bool sqlDeleteSMS(int idsms) {
try {
pgsql.sql(
"delete from da_sms where da_id = ?", idsms
);
} catch (Exception e) {
log.e("Ошибка удаления SMS в БД. " ~ e.msg);
return false;
}
return true;
}
/***********************************************************
Запросы для таблицы USSD
***********************************************************/
USSDDB[] sqlGetUSSDNumbers() {
USSDDB[] numbers;
try {
auto queryResult = pgsql.sql(
"select distinct da_to from da_ussd"
);
foreach (row; queryResult) {
USSDDB data;
data.to = row["da_to"];
numbers ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return numbers;
}
USSDDB[] sqlGetListUSSD(string to) {
USSDDB[] ussd;
try {
auto queryResult = pgsql.sql(
"select
dau.da_id,
to_char(dau.da_date, 'YYYY.MM.DD HH24:MI:SS') da_date,
dau.da_to,
dau.da_type,
daut.da_comment da_type_comment,
dau.da_text
from da_ussd dau
inner join da_ussd_type daut
on dau.da_type = daut.da_id
where dau.da_to = ?
order by dau.da_date desc",
to
);
foreach (row; queryResult) {
USSDDB data;
data.id = row["da_id"].to!int;
data.date = row["da_date"];
data.to = row["da_to"];
data.type = row["da_type"].to!int;
data.type_comment = row["da_type_comment"];
data.text = row["da_text"];
ussd ~= data;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return ussd;
}
USSDDB sqlGetUSSD(int idussd) {
USSDDB data;
try {
auto queryResult = pgsql.sql(
"select
da_id,
to_char(da_date, 'YYYY.MM.DD HH24:MI:SS') da_date,
da_to,
da_type,
da_text
from da_ussd
where da_id = ?",
idussd
);
foreach (row; queryResult) {
data.id = row["da_id"].to!int;
data.date = row["da_date"];
data.to = row["da_to"];
data.type = row["da_type"].to!int;
data.text = row["da_text"];
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return data;
}
bool sqlDeleteUSSD(int idussd) {
try {
pgsql.sql(
"delete from da_ussd where da_id = ?", idussd
);
} catch (Exception e) {
log.e("Ошибка удаления USSD в БД. " ~ e.msg);
return false;
}
return true;
}
/***********************************************************
Запросы для таблицы информации о сервере
***********************************************************/
ServerDB sqlGetServerInfo() {
ServerDB server;
try {
auto queryResult = pgsql.sql(
"select
case when da_transparent_mode then 1 else 0 end da_transparent_mode,
da_internal_number,
da_external_number,
case when da_external_number_on then 1 else 0 end da_external_number_on
from da_server
where da_id = 1"
);
foreach (row; queryResult) {
server.transparent_mode = row["da_transparent_mode"].to!int.to!bool;
server.internal_number = row["da_internal_number"];
server.external_number = row["da_external_number"];
server.external_number_on = row["da_external_number_on"].to!int.to!bool;
}
} catch (Exception e) {
log.e("Не удалось выполнить запрос к БД. " ~ e.msg);
}
return server;
}
bool sqlUpdateServerInfo(ServerDB server) {
try {
pgsql.sql(
"update da_server set
da_transparent_mode = ?,
da_internal_number = ?,
da_external_number = ?,
da_external_number_on = ?
where da_id = 1",
server.transparent_mode,
server.internal_number,
server.external_number,
server.external_number_on
);
} catch (Exception e) {
log.e("Ошибка обновления параметров сервера в БД. " ~ e.msg);
return false;
}
return true;
}

View file

@ -4,15 +4,6 @@ struct ServerInfo {
string name; string name;
} }
struct AuthData {
string login;
string password;
}
struct UserData {
bool login = false;
}
struct WebHost { struct WebHost {
string[] addresses; string[] addresses;
ushort http = 0; ushort http = 0;
@ -31,11 +22,6 @@ struct GroupDB {
string comment; string comment;
} }
struct ListDB {
string name;
string comment;
}
struct NumberDB { struct NumberDB {
string number; string number;
string group; string group;
@ -45,27 +31,3 @@ struct NumberDB {
int black_cc; int black_cc;
string comment; string comment;
} }
struct SMSDB {
int id;
string date;
string to;
string from;
string text;
}
struct USSDDB {
int id;
string date;
string to;
int type;
string type_comment;
string text;
}
struct ServerDB {
bool transparent_mode;
string internal_number;
string external_number;
bool external_number_on;
}

View file

@ -1,3 +1,3 @@
module version_; module version_;
enum dasterVersion = "v0.1.0-beta.1"; enum dasterVersion = "v0.0.1";

View file

@ -1,8 +0,0 @@
doctype html
head
title Страница не найдена
link(rel='icon', type='image/png', sizes='128x128', href='favicon.png')
link(rel='stylesheet', type='text/css', href='404.css')
body
div.page
div.pic

View file

@ -1,26 +0,0 @@
doctype html
head
title #{serverInfo.name}: авторизация
link(rel='icon', type='image/png', sizes='128x128', href='favicon.png')
link(rel='stylesheet', type='text/css', href='jquery-ui.min.css')
link(rel='stylesheet', type='text/css', href='authorization.css')
script(src='jquery-3.7.0.min.js')
script(src='jquery-ui.min.js')
script(src='noticer.min.js')
script(src='authorization.js')
body
div.form
div.logo
div.title #{serverInfo.name}
table
tbody
tr
td.label Логин:
td
input.input-focus#login(type='text')
tr
td.label Пароль:
td
input.input-focus#password(type='password')
div.div-button
button#authorization Войти

View file

@ -0,0 +1,22 @@
- import structures;
table
thead.head
tr
th Номер
th Список
th Общие звонки
th Белые звонки
th Черные звонки
th Комментарий
div.body-rows
table
tbody.body
- foreach (number; listNumbers)
tr.row
td #{number.number}
td #{number.list}
td #{number.all_cc}
td #{number.white_cc}
td #{number.black_cc}
td #{number.comment}

View file

@ -6,21 +6,19 @@ head
link(rel='stylesheet', type='text/css', href='style.css') link(rel='stylesheet', type='text/css', href='style.css')
script(src='jquery-3.7.0.min.js') script(src='jquery-3.7.0.min.js')
script(src='jquery-ui.min.js') script(src='jquery-ui.min.js')
script(src='noticer.min.js') script(src='message.js')
script(src='script.js') script(src='script.js')
body body
div.div-header div.div-header
div.div-add.main-button // div.div-add
button#add-number Добавить номер // button.addNumber Добавить номер
div.div-search.main-button div.div-search
input.input-focus#search(name='search', type='text', value='', placeholder='Найти номер') input.input-focus.search(name='search', type='text', value='', placeholder='Найти номер')
div.div-update-tab.main-button // div.div-user Вы вошли как #{user.name}
button#update-tab Перезагрузить div.div-user Вы вошли как Александр
div.div-update-tab.main-button div.div-button
button#update-group Обновить button Выход
div.div-user // button(onclick='logout()') Выход
div.div-logout
button#logout Выход
div.content div.content
div#tabs div#tabs
ul ul
@ -34,5 +32,8 @@ body
a(href='#tabs-server') Сервер a(href='#tabs-server') Сервер
div#tabs-numbers div#tabs-numbers
div#tabs-sms div#tabs-sms
p Список SMS сообщений
div#tabs-ussd div#tabs-ussd
p Список результатов USSD запросов
div#tabs-server div#tabs-server
p Информация о сервере

View file

@ -1,16 +0,0 @@
div#accordion-numbers
- foreach (group; listGroups)
h3 #{group.comment}
div.group-content(data-group-name='#{group.name}')
table.table-content
thead.head
tr
th Номер
th Список
th Общие звонки
th Белые звонки
th Черные звонки
th Комментарий
div.body-rows
table.table-content
tbody.body

View file

@ -1,13 +0,0 @@
div#accordion-sms
- foreach (number; numbers)
h3 На номер #{number.to}
div.group-content(data-to='#{number.to}')
table.table-content
thead.head
tr
th.sms-content-width Дата
th.sms-content-width От кого
th Текст сообщения
div.body-rows
table.table-content
tbody.body

View file

@ -1,13 +0,0 @@
div#accordion-ussd
- foreach (number; numbers)
h3 На номер #{number.to}
div.group-content(data-to='#{number.to}')
table.table-content
thead.head
tr
th.ussd-content-width Дата
th.ussd-content-width-type Тип
th Текст сообщения
div.body-rows
table.table-content
tbody.body

View file

@ -1,42 +0,0 @@
div#number-data
table
tbody
tr
td.number-label Номер:
td.number-value
- if (edit)
input.number-input-main.input-focus#number-number(type='text', value='#{dataNumber.number}', disabled)
- else
input.number-input-main.input-focus#number-number(type='text', value='#{dataNumber.number}')
td.number-label Общие звонки:
td.number-value
input.number-input-cc.input-focus#number-all-cc(type='text', value='#{dataNumber.all_cc}')
tr
td.number-label Группа:
td.number-value
select#number-group
- foreach (group; groups)
- if (dataNumber.group == group.name)
option(selected, value='#{group.name}') #{group.comment}
- else
option(value='#{group.name}') #{group.comment}
td.number-label Белые звонки:
td.number-value
input.number-input-cc.input-focus#number-white-cc(type='text', value='#{dataNumber.white_cc}')
tr
td.number-label Список:
td.number-value
select#number-list
- foreach (list; lists)
- if (dataNumber.list == list.name)
option(selected, value='#{list.name}') #{list.comment}
- else
option(value='#{list.name}') #{list.comment}
td.number-label Черные звонки:
td.number-value
input.number-input-cc.input-focus#number-black-cc(type='text', value='#{dataNumber.black_cc}')
div.div-advanced
div.comment
div.comment-name Комментарий
div.comment-content
textarea.input-focus#number-comment #{dataNumber.comment}

View file

@ -1,23 +0,0 @@
div#server-data
table
tbody
tr
td.server-label Внутренний номер:
td.server-value
input.input-focus.server-input#server-internal-number(type="text", value="#{dataServer.internal_number}")
td.server-value
tr
td.server-label Внешний номер:
td.server-value
input.input-focus.server-input#server-external-number(type="text", value="#{dataServer.external_number}")
td.server-value
label(for="server-external-number-on") Использовать
input.server-input#server-external-number-on(name="server-external-number-on", type="checkbox", checked="#{dataServer.external_number_on}")
tr
td.server-label Прозрачный режим:
td.server-value
label(for="server-transparent-mode") Включен
input.server-input#server-transparent-mode(name="server-transparent-mode", type="checkbox", checked="#{dataServer.transparent_mode}")
td.server-value
div.server-button
button#server-button Сохранить изменения

View file

@ -1,11 +0,0 @@
div#sms-data
table
tbody
tr
td.sms-label Дата:
td.sms-value #{dataSMS.date}
tr
td.sms-label.sms-label-text Текст:
td.sms-value
div.sms-content
textarea#sms-content(readonly, data-id="#{dataSMS.id}", data-from="#{dataSMS.from}") #{dataSMS.text}

View file

@ -1,11 +0,0 @@
div#ussd-data
table
tbody
tr
td.ussd-label Дата:
td.ussd-value #{dataUSSD.date}
tr
td.ussd-label.ussd-label-text Текст:
td.ussd-value
div.ussd-content
textarea#ussd-content(readonly, data-id="#{dataUSSD.id}") #{dataUSSD.text}