v0.0.1
Разработка: - БД скрипт с начальными данными - вывод списка номеров телефонов по группам
							
								
								
									
										19
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1 +1,20 @@
 | 
			
		|||
/database/volumes-data
 | 
			
		||||
.dub
 | 
			
		||||
docs.json
 | 
			
		||||
__dummy.html
 | 
			
		||||
docs/
 | 
			
		||||
/web
 | 
			
		||||
web.so
 | 
			
		||||
web.dylib
 | 
			
		||||
web.dll
 | 
			
		||||
web.a
 | 
			
		||||
web.lib
 | 
			
		||||
web-test-*
 | 
			
		||||
*.exe
 | 
			
		||||
*.o
 | 
			
		||||
*.obj
 | 
			
		||||
*.lst
 | 
			
		||||
bin
 | 
			
		||||
web.log
 | 
			
		||||
settings.conf
 | 
			
		||||
.vscode
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										33
									
								
								certs/test.local.crt
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
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
 | 
			
		||||
WwU=
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
							
								
								
									
										51
									
								
								certs/test.local.key
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
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
 | 
			
		||||
-----END RSA PRIVATE KEY-----
 | 
			
		||||
| 
						 | 
				
			
			@ -9,9 +9,15 @@ services:
 | 
			
		|||
      POSTGRES_PASSWORD: asterisk
 | 
			
		||||
      PGTZ: Europe/Moscow
 | 
			
		||||
      TZ: Europe/Moscow
 | 
			
		||||
    networks:
 | 
			
		||||
      - db_net
 | 
			
		||||
    ports:
 | 
			
		||||
      - 5432:5432
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./volumes-data:/var/lib/postgresql/data
 | 
			
		||||
      - /etc/timezone:/etc/timezone:ro
 | 
			
		||||
      - /etc/localtime:/etc/localtime:ro
 | 
			
		||||
 | 
			
		||||
networks: 
 | 
			
		||||
  db_net:
 | 
			
		||||
    name: db_net
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,62 +1,62 @@
 | 
			
		|||
create table if not exists "groups" (
 | 
			
		||||
    "name" varchar(20) not null,
 | 
			
		||||
    "comment" varchar(100) default null,
 | 
			
		||||
    constraint groups_pk primary key ("name") 
 | 
			
		||||
create table if not exists da_groups (
 | 
			
		||||
	da_name varchar(20) not null,
 | 
			
		||||
	da_comment varchar(100) default null,
 | 
			
		||||
	constraint da_groups_pk primary key (da_name) 
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
insert into "groups" ("name", "comment")
 | 
			
		||||
    values
 | 
			
		||||
        ('general', 'Общие контакты'),
 | 
			
		||||
        ('work', 'Рабочие контакты'),
 | 
			
		||||
        ('personal', 'Личные контакты');
 | 
			
		||||
insert into da_groups (da_name, da_comment)
 | 
			
		||||
	values
 | 
			
		||||
		('general', 'Общие контакты'),
 | 
			
		||||
		('work', 'Рабочие контакты'),
 | 
			
		||||
		('personal', 'Личные контакты');
 | 
			
		||||
 | 
			
		||||
create table if not exists lists (
 | 
			
		||||
    "name" varchar(20) not null,
 | 
			
		||||
    "comment" varchar(100) default null,
 | 
			
		||||
    constraint lists_pk primary key ("name") 
 | 
			
		||||
create table if not exists da_lists (
 | 
			
		||||
	da_name varchar(20) not null,
 | 
			
		||||
	da_comment varchar(100) default null,
 | 
			
		||||
	constraint da_lists_pk primary key (da_name) 
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
insert into lists ("name", "comment")
 | 
			
		||||
    values
 | 
			
		||||
        ('general', 'Общий список'),
 | 
			
		||||
        ('whitelist', 'Белый список'),
 | 
			
		||||
        ('blacklist', 'Черный список');
 | 
			
		||||
insert into da_lists (da_name, da_comment)
 | 
			
		||||
	values
 | 
			
		||||
		('general', 'Общий'),
 | 
			
		||||
		('whitelist', 'Белый'),
 | 
			
		||||
		('blacklist', 'Черный');
 | 
			
		||||
 | 
			
		||||
create table if not exists numbers (
 | 
			
		||||
    "number" varchar(12) not null,
 | 
			
		||||
    "group" varchar(20) not null default 'general',
 | 
			
		||||
    list varchar(20) not null default 'general',
 | 
			
		||||
    all_cc int not null default 0,
 | 
			
		||||
    white_cc int not null default 0,
 | 
			
		||||
    black_cc int not null default 0,  
 | 
			
		||||
    "comment" varchar(100) default null,
 | 
			
		||||
    constraint numbers_pk primary key ("number"),
 | 
			
		||||
    foreign key ("group") references "groups" ("name") on delete set null on update cascade,
 | 
			
		||||
    foreign key (list) references lists ("name") on delete set null on update cascade
 | 
			
		||||
create table if not exists da_numbers (
 | 
			
		||||
	da_number varchar(12) not null,
 | 
			
		||||
	da_group varchar(20) not null default 'general',
 | 
			
		||||
	da_list varchar(20) not null default 'general',
 | 
			
		||||
	da_all_cc int not null default 0,
 | 
			
		||||
	da_white_cc int not null default 0,
 | 
			
		||||
	da_black_cc int not null default 0,  
 | 
			
		||||
	da_comment varchar(100) default null,
 | 
			
		||||
	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_list) references da_lists (da_name) on delete set null on update cascade
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
create table if not exists sms (
 | 
			
		||||
    id bigserial not null,
 | 
			
		||||
    "date" timestamp not null default NOW(),
 | 
			
		||||
    "to" varchar(12) not null,
 | 
			
		||||
    "from" varchar(12) not null,
 | 
			
		||||
    "text" text not null,
 | 
			
		||||
    constraint sms_pk primary key (id)
 | 
			
		||||
create table if not exists da_sms (
 | 
			
		||||
    da_id bigserial not null,
 | 
			
		||||
    da_date timestamp not null default NOW(),
 | 
			
		||||
    da_to varchar(12) not null,
 | 
			
		||||
    da_from varchar(12) not null,
 | 
			
		||||
    da_text text not null,
 | 
			
		||||
    constraint da_sms_pk primary key (da_id)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
create table if not exists ussd (
 | 
			
		||||
    id bigserial not null,
 | 
			
		||||
    "date" timestamp not null default NOW(),
 | 
			
		||||
    "to" varchar(12) not null,
 | 
			
		||||
    "type" smallint not null,
 | 
			
		||||
    "text" text not null,
 | 
			
		||||
    constraint ussd_pk primary key (id)
 | 
			
		||||
create table if not exists da_ussd (
 | 
			
		||||
    da_id bigserial not null,
 | 
			
		||||
    da_date timestamp not null default NOW(),
 | 
			
		||||
    da_to varchar(12) not null,
 | 
			
		||||
    da_type smallint not null,
 | 
			
		||||
    da_text text not null,
 | 
			
		||||
    constraint da_ussd_pk primary key (da_id)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
create table if not exists "server" (
 | 
			
		||||
    address varchar(50) not null,
 | 
			
		||||
    transparent_mode bool not null default false,
 | 
			
		||||
    internal_number varchar(12) not null,
 | 
			
		||||
    external_number varchar(12) not null,
 | 
			
		||||
    constraint server_pk primary key (address)
 | 
			
		||||
create table if not exists da_server (
 | 
			
		||||
    da_address varchar(50) not null,
 | 
			
		||||
    da_transparent_mode bool not null default false,
 | 
			
		||||
    da_internal_number varchar(12) not null,
 | 
			
		||||
    da_external_number varchar(12) not null,
 | 
			
		||||
    constraint da_server_pk primary key (da_address)
 | 
			
		||||
);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										33
									
								
								dub.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
{
 | 
			
		||||
    "authors": [
 | 
			
		||||
        "Alexander Zhirov"
 | 
			
		||||
    ],
 | 
			
		||||
    "copyright": "Copyright © 2023, Alexander Zhirov",
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "vibe-d": "~>0.9",
 | 
			
		||||
        "ldap": "~>0.4",
 | 
			
		||||
        "singlog": "~>0.3.1",
 | 
			
		||||
        "arsd-official:postgres": "~>10.9.10",
 | 
			
		||||
        "readconf": "~>0.3.1"
 | 
			
		||||
    },
 | 
			
		||||
    "buildTypes": {
 | 
			
		||||
        "debug": {
 | 
			
		||||
            "buildOptions": [
 | 
			
		||||
                "debugMode",
 | 
			
		||||
                "debugInfo"
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "release": {
 | 
			
		||||
            "buildOptions": [
 | 
			
		||||
                "releaseMode",
 | 
			
		||||
                "inline",
 | 
			
		||||
                "optimize"
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "description": "Dialplan Asterisk - веб-сервер для управления обработкой вызовов Asterisk",
 | 
			
		||||
    "license": "proprietary",
 | 
			
		||||
    "name": "daster",
 | 
			
		||||
    "targetPath": "bin",
 | 
			
		||||
    "targetType": "executable"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								dub.selections.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
{
 | 
			
		||||
	"fileVersion": 1,
 | 
			
		||||
	"versions": {
 | 
			
		||||
		"arsd-official": "10.9.10",
 | 
			
		||||
		"datefmt": "1.0.4",
 | 
			
		||||
		"diet-ng": "1.8.1",
 | 
			
		||||
		"eventcore": "0.9.25",
 | 
			
		||||
		"ldap": "0.4.0",
 | 
			
		||||
		"libasync": "0.8.6",
 | 
			
		||||
		"memutils": "1.0.9",
 | 
			
		||||
		"mir-linux-kernel": "1.0.1",
 | 
			
		||||
		"openssl": "3.3.0",
 | 
			
		||||
		"openssl-static": "1.0.2+3.0.8",
 | 
			
		||||
		"readconf": "0.3.1",
 | 
			
		||||
		"silly": "1.1.1",
 | 
			
		||||
		"singlog": "0.3.1",
 | 
			
		||||
		"stdx-allocator": "2.77.5",
 | 
			
		||||
		"taggedalgebraic": "0.11.22",
 | 
			
		||||
		"vibe-core": "2.2.0",
 | 
			
		||||
		"vibe-d": "0.9.6"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								dub.settings.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
{
 | 
			
		||||
	"defaultArchitecture": "x86_64",
 | 
			
		||||
	"defaultCompiler": "ldc2"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								images/favicon.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								jq/images/ui-icons_444444_256x240.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								jq/images/ui-icons_555555_256x240.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								jq/images/ui-icons_777620_256x240.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								jq/images/ui-icons_777777_256x240.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								jq/images/ui-icons_cc0000_256x240.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								jq/images/ui-icons_ffffff_256x240.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.4 KiB  | 
							
								
								
									
										2
									
								
								jq/jquery-3.7.0.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										7
									
								
								jq/jquery-ui.min.css
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										6
									
								
								jq/jquery-ui.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										100
									
								
								js/message.js
									
										
									
									
									
										Normal 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								js/script.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
$(document).ready(function () {
 | 
			
		||||
    message = new Message;
 | 
			
		||||
 | 
			
		||||
    // (new divNotFoundNumbers).push("Загрузка...");
 | 
			
		||||
 | 
			
		||||
    $("button").button();
 | 
			
		||||
    $("#tabs").tabs();
 | 
			
		||||
    // $("#accordion-numbers").accordion();
 | 
			
		||||
 | 
			
		||||
    // $(".addNumber").click(() => {
 | 
			
		||||
    //     numberAdd()
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    $("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 = {}) {
 | 
			
		||||
    let response = await fetch('.', {
 | 
			
		||||
        method: 'POST',
 | 
			
		||||
        headers: {
 | 
			
		||||
            'Content-Type': 'application/json;charset=utf-8'
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify({
 | 
			
		||||
            ...queryData, 
 | 
			
		||||
            query: query
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!response.ok)
 | 
			
		||||
        throw new Error(`Произошла неизвестаня ошибка: ${response.status}`);
 | 
			
		||||
    
 | 
			
		||||
    const data = await response[type]();
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isJSON(str) {
 | 
			
		||||
    try {
 | 
			
		||||
        return (JSON.parse(str) && !!str);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loadData() {
 | 
			
		||||
    request('listsgroups', 'json').then(data => {
 | 
			
		||||
        data.error ? message.error(data.message) : generateListsGroups(data);
 | 
			
		||||
    }).catch(error => {
 | 
			
		||||
        message.error(error.message);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateListsGroups(data) {
 | 
			
		||||
    let numbers = $("#tabs-numbers");
 | 
			
		||||
    let group = $('<div id="accordion-numbers"></div>');
 | 
			
		||||
    $(data).each((i, j) => {
 | 
			
		||||
        group.append(`<h3>${j.comment}</h3><div class="group-content" data-group-name="${j.name}"></div>`);
 | 
			
		||||
    });
 | 
			
		||||
    numbers.append(group);
 | 
			
		||||
    $("#accordion-numbers").accordion({
 | 
			
		||||
        heightStyle: "content",
 | 
			
		||||
        create: function( event, ui ) {
 | 
			
		||||
            generateGroupNumbers(ui.panel);
 | 
			
		||||
        },
 | 
			
		||||
        beforeActivate: function( event, ui ) {
 | 
			
		||||
            generateGroupNumbers(ui.newPanel);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function generateGroupNumbers(panel) {
 | 
			
		||||
    request('groupnumbers', 'text', { group: panel.data("group-name") }).then(data => {
 | 
			
		||||
        if (isJSON(data) && JSON.parse(data).error)
 | 
			
		||||
            message.error(JSON.parse(data).message);
 | 
			
		||||
        else {
 | 
			
		||||
            panel.html(data);
 | 
			
		||||
        }
 | 
			
		||||
    }).catch(error => {
 | 
			
		||||
        message.error(error.message);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								public/Scada-Regular.ttf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										107
									
								
								public/style.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
@font-face {
 | 
			
		||||
    font-family: Scada;
 | 
			
		||||
    src: url(Scada-Regular.ttf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    height: 100vh;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    color: #333;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    font-family: Scada;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.div-header {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    width: 60%;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    margin-top: 30px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* HEADER */
 | 
			
		||||
 | 
			
		||||
/* div.div-add {
 | 
			
		||||
    margin-right: 20px
 | 
			
		||||
} */
 | 
			
		||||
 | 
			
		||||
div.div-search {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    height: 100%
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.div-user {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input-focus {
 | 
			
		||||
    border: 1px solid#c5c5c5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input-focus:hover {
 | 
			
		||||
    border: 1px solid #ccc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input-focus::placeholder {
 | 
			
		||||
    color: #333
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input-focus:focus {
 | 
			
		||||
    outline: none;
 | 
			
		||||
    box-shadow: 1px 1px 10px 1px #007fff;
 | 
			
		||||
    border: 1px solid #003eff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* BODY */
 | 
			
		||||
 | 
			
		||||
.content {
 | 
			
		||||
    width: 60%;
 | 
			
		||||
    margin-top: 20px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content table {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    border-collapse: collapse;
 | 
			
		||||
    border-spacing: 0;
 | 
			
		||||
    table-layout: fixed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.body-rows {
 | 
			
		||||
    max-height: 55vh;
 | 
			
		||||
	overflow-x: auto;
 | 
			
		||||
    border: 1px solid #c5c5c5;
 | 
			
		||||
    border-top: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
    color: #333;
 | 
			
		||||
    height: 50px;
 | 
			
		||||
    background-color: #f6f6f6;
 | 
			
		||||
    border: 1px solid #c5c5c5;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.body-rows tbody tr:nth-child(even){
 | 
			
		||||
	background: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tr.row:hover, tr.row:nth-child(even):hover {
 | 
			
		||||
    background-color: #c5c5c5;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								settings.conf.sample
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
[web-host]
 | 
			
		||||
title           => "Управление диалпланом"
 | 
			
		||||
addresses       => 127.0.0.1
 | 
			
		||||
http            => 8080
 | 
			
		||||
https           => 443
 | 
			
		||||
cert            => certs/test.local.crt
 | 
			
		||||
key             => certs/test.local.key
 | 
			
		||||
data            => ./                                   ; Путь к каталогу, в котором расположены каталоги public, js, images
 | 
			
		||||
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
 | 
			
		||||
logfile         => /var/log/jaster.log                  ; if log-output set with 4
 | 
			
		||||
 | 
			
		||||
[daster-db]
 | 
			
		||||
host            => 127.0.0.1
 | 
			
		||||
port            => 5432
 | 
			
		||||
dbname          => daster
 | 
			
		||||
user            => daster
 | 
			
		||||
password        => daster
 | 
			
		||||
							
								
								
									
										405
									
								
								source/daster.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,405 @@
 | 
			
		|||
module daster;
 | 
			
		||||
 | 
			
		||||
import vibe.vibe;
 | 
			
		||||
import singlog;
 | 
			
		||||
import readconf;
 | 
			
		||||
 | 
			
		||||
import core.stdc.stdlib : exit, EXIT_SUCCESS, EXIT_FAILURE;
 | 
			
		||||
import std.stdio: writefln;
 | 
			
		||||
import std.getopt;
 | 
			
		||||
import std.file;
 | 
			
		||||
import std.path;
 | 
			
		||||
import std.algorithm;
 | 
			
		||||
import std.array;
 | 
			
		||||
 | 
			
		||||
import verinfo;
 | 
			
		||||
import pgdb;
 | 
			
		||||
import structures;
 | 
			
		||||
 | 
			
		||||
import requests.listsgroups;
 | 
			
		||||
import requests.groupnumbers;
 | 
			
		||||
 | 
			
		||||
static ServerInfo serverInfo;
 | 
			
		||||
 | 
			
		||||
private void showVersion() {
 | 
			
		||||
    writefln("daster версия %s, собрано %s", getDasterVersion(), __DATE__);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(string[] args) {
 | 
			
		||||
    log.level(log.INFORMATION);
 | 
			
		||||
    log.output(log.SYSLOG);
 | 
			
		||||
 | 
			
		||||
    bool flagVersion;
 | 
			
		||||
    string flagSettings;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        auto opt = getopt(
 | 
			
		||||
            args,
 | 
			
		||||
            config.bundling,
 | 
			
		||||
            config.caseSensitive,
 | 
			
		||||
            "settings|s",
 | 
			
		||||
                "<файл> Путь к файлу конфигурации settings.conf",
 | 
			
		||||
                    &flagSettings,
 | 
			
		||||
            "version|v",
 | 
			
		||||
                "Текущая версия программы",
 | 
			
		||||
                    &flagVersion
 | 
			
		||||
        );
 | 
			
		||||
        if (opt.helpWanted) {
 | 
			
		||||
            showVersion();
 | 
			
		||||
            defaultGetoptPrinter(
 | 
			
		||||
                "Использование: daster [ОПЦИИ]...\n",
 | 
			
		||||
                    opt.options
 | 
			
		||||
            );
 | 
			
		||||
            return EXIT_SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
        if (flagVersion) {
 | 
			
		||||
            showVersion();
 | 
			
		||||
            return EXIT_SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
    } catch (GetOptException e) {
 | 
			
		||||
        showVersion();
 | 
			
		||||
        log.c(e.msg);
 | 
			
		||||
        log.i("Попробуйте 'daster -h' для дополнительной информации");
 | 
			
		||||
        log.i("https://git.zhirov.kz/alexander/daster");
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!flagSettings.length)
 | 
			
		||||
        flagSettings = "./settings.conf";
 | 
			
		||||
 | 
			
		||||
    if (!flagSettings.exists) {
 | 
			
		||||
        log.c("Файл конфигурации не найден: " ~ flagSettings);
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rc.read(flagSettings);
 | 
			
		||||
    rcAsteriskDB();
 | 
			
		||||
    auto webHost = rcWebHost();
 | 
			
		||||
 | 
			
		||||
    serverInfo = ServerInfo(webHost.title);
 | 
			
		||||
 | 
			
		||||
    if (webHost.loglevel != -1) log.level(webHost.loglevel);
 | 
			
		||||
    if (webHost.logoutput) log.output(webHost.logoutput);
 | 
			
		||||
    if (webHost.logfile.length) log.file(webHost.logfile);
 | 
			
		||||
 | 
			
		||||
    auto router = new URLRouter;
 | 
			
		||||
    router.post("/", &postReq);
 | 
			
		||||
    router.get("/", &getReq);
 | 
			
		||||
    router.get("*", serveStaticFiles(buildPath(webHost.data, "public")));
 | 
			
		||||
    router.get("*", serveStaticFiles(buildPath(webHost.data, "images")));
 | 
			
		||||
    router.get("*", serveStaticFiles(buildPath(webHost.data, "js")));
 | 
			
		||||
    router.get("*", serveStaticFiles(buildPath(webHost.data, "jq")));
 | 
			
		||||
 | 
			
		||||
    auto memorySessionStore = new MemorySessionStore;
 | 
			
		||||
 | 
			
		||||
    auto settingsHTTP = new HTTPServerSettings;
 | 
			
		||||
    auto settingsHTTPS = new HTTPServerSettings;
 | 
			
		||||
 | 
			
		||||
    if (webHost.http) {
 | 
			
		||||
        settingsHTTP.sessionStore = memorySessionStore;
 | 
			
		||||
        settingsHTTP.port = webHost.http;
 | 
			
		||||
        settingsHTTP.bindAddresses = ["::1"] ~ webHost.addresses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (webHost.https) {
 | 
			
		||||
        settingsHTTPS.sessionStore = memorySessionStore;
 | 
			
		||||
        settingsHTTPS.port = webHost.https;
 | 
			
		||||
        settingsHTTPS.bindAddresses = ["::1"] ~ webHost.addresses;
 | 
			
		||||
        settingsHTTPS.tlsContext = createTLSContext(TLSContextKind.server);
 | 
			
		||||
        settingsHTTPS.tlsContext.useCertificateChainFile(webHost.cert);
 | 
			
		||||
        settingsHTTPS.tlsContext.usePrivateKeyFile(webHost.key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    startWebServer(webHost, settingsHTTP, settingsHTTPS, router);
 | 
			
		||||
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void startWebServer(WebHost wh, HTTPServerSettings http, HTTPServerSettings https, URLRouter router)
 | 
			
		||||
{
 | 
			
		||||
    if (wh.http && wh.https) {
 | 
			
		||||
        auto listenerHTTP = listenHTTP(http, router);
 | 
			
		||||
        auto listenerHTTPS = listenHTTP(https, router);
 | 
			
		||||
        scope (exit) { listenerHTTP.stopListening(); }
 | 
			
		||||
        scope (exit) { listenerHTTPS.stopListening(); }
 | 
			
		||||
        runApplication();
 | 
			
		||||
    } else if (wh.http) {
 | 
			
		||||
        auto listenerHTTP = listenHTTP(http, router);
 | 
			
		||||
        scope (exit) { listenerHTTP.stopListening(); }
 | 
			
		||||
        runApplication();
 | 
			
		||||
    } else if (wh.https) {
 | 
			
		||||
        auto listenerHTTPS = listenHTTP(https, router);
 | 
			
		||||
        scope (exit) { listenerHTTPS.stopListening(); }
 | 
			
		||||
        runApplication();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void getReq(HTTPServerRequest req, HTTPServerResponse res) {
 | 
			
		||||
    // if (req.session) {
 | 
			
		||||
    //     auto user = req.session.get!UserData("userData");
 | 
			
		||||
    //     if (user.loggedIn) {
 | 
			
		||||
    //         renderMainPage(req, res);
 | 
			
		||||
    //         return;
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
    
 | 
			
		||||
    // render!("index.dt", serverInfo)(res);
 | 
			
		||||
    renderMainPage(req, res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void renderMainPage(HTTPServerRequest req, HTTPServerResponse res) {
 | 
			
		||||
    render!("index.dt", serverInfo)(res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void postReq(HTTPServerRequest req, HTTPServerResponse res) {
 | 
			
		||||
    if (req.method != HTTPMethod.POST) return;
 | 
			
		||||
 | 
			
		||||
    auto jsr = req.json;
 | 
			
		||||
    string query = jsr["query"].get!string;
 | 
			
		||||
 | 
			
		||||
    if (query.empty) return;
 | 
			
		||||
 | 
			
		||||
    // if (query != "authorization" && !checkAuth(req)) {
 | 
			
		||||
    //     res.send(
 | 
			
		||||
    //         true,
 | 
			
		||||
    //         "Сессия не существует. Перезагрузите страницу"
 | 
			
		||||
    //     );
 | 
			
		||||
    //     return;
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    switch (query) {
 | 
			
		||||
        case "listsgroups":
 | 
			
		||||
            listsgroups(req, res);
 | 
			
		||||
            break;
 | 
			
		||||
        case "groupnumbers":
 | 
			
		||||
            groupnumbers(req, res);
 | 
			
		||||
            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:
 | 
			
		||||
            res.redirect("/");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rcAsteriskDB() {
 | 
			
		||||
    ConfigSection asteriskDB;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        asteriskDB = rc[]["daster-db"];
 | 
			
		||||
    } catch (Exception e) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки daster-db");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (asteriskDB["host"].empty) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки daster-db.host");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (asteriskDB["port"].empty) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки daster-db.port");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (asteriskDB["dbname"].empty) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки daster-db.dbname");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (asteriskDB["user"].empty) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки daster-db.user");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        pgsql(
 | 
			
		||||
            "hostaddr=" ~ asteriskDB["host"] ~
 | 
			
		||||
            " port=" ~ asteriskDB["port"] ~
 | 
			
		||||
            " dbname=" ~ asteriskDB["dbname"] ~
 | 
			
		||||
            " user=" ~ asteriskDB["user"] ~
 | 
			
		||||
            " password=" ~ asteriskDB["password"]
 | 
			
		||||
        );
 | 
			
		||||
    } catch (Exception e) {
 | 
			
		||||
        log.c(e);
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WebHost rcWebHost() {
 | 
			
		||||
    WebHost wh;
 | 
			
		||||
    ConfigSection webHost;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        webHost = rc[]["web-host"];
 | 
			
		||||
    } catch (Exception e) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки web-host");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (webHost["addresses"].empty) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки web-host.addresses");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        wh.addresses = webHost["addresses"].to!string.split(',').map!(a => a.strip).array;
 | 
			
		||||
    } catch (Exception e) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки web-host.addresses");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (webHost["http"].empty && webHost["https"].empty) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки web-host.[http|https]: 
 | 
			
		||||
            должен быть указан хотя бы один протокол");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["http"].empty) {
 | 
			
		||||
        try {
 | 
			
		||||
            wh.http = webHost["http"].to!ushort;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.http");
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["https"].empty) {
 | 
			
		||||
        try {
 | 
			
		||||
            wh.https = webHost["https"].to!ushort;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.https");
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["https"].empty) {
 | 
			
		||||
        if (webHost["cert"].empty || webHost["key"].empty) {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.[cert|key]: 
 | 
			
		||||
                необходимо указать сертификат и ключ для использования https протокола"
 | 
			
		||||
            );
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        wh.cert = webHost["cert"];
 | 
			
		||||
    } catch (Exception e) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки web-host.cert");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        wh.key = webHost["key"];
 | 
			
		||||
    } catch (Exception e) {
 | 
			
		||||
        log.c("В конфигурационном файле не верны настройки web-host.key");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["data"].empty) {
 | 
			
		||||
        string data;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            data = webHost["data"];
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.data");
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (data.exists) {
 | 
			
		||||
            if (!buildPath(data, "public").exists) {
 | 
			
		||||
                log.c("В конфигурационном файле не верны настройки web-host.data - каталог не существует: public");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            if (!buildPath(data, "js").exists) {
 | 
			
		||||
                log.c("В конфигурационном файле не верны настройки web-host.data - каталог не существует: js");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            if (!buildPath(data, "images").exists) {
 | 
			
		||||
                log.c("В конфигурационном файле не верны настройки web-host.data - каталог не существует: images");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.data - каталог не существует: " ~ data);
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wh.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["title"].empty) {
 | 
			
		||||
        try {
 | 
			
		||||
            wh.title = webHost["title"];
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.w("Заголовок не был установлен - web-host.title");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["loglevel"].empty) {
 | 
			
		||||
        try {
 | 
			
		||||
            wh.loglevel = webHost["loglevel"].to!int;
 | 
			
		||||
            if (wh.loglevel < 0 || wh.loglevel > 6)
 | 
			
		||||
                throw new Exception("несуществующий уровень");
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.loglevel: " ~ e.msg);
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!webHost["logoutput"].empty) {
 | 
			
		||||
        try {
 | 
			
		||||
            wh.logoutput = webHost["logoutput"]
 | 
			
		||||
                .to!string.split(',')
 | 
			
		||||
                .map!((a) {
 | 
			
		||||
                    auto flag = a.strip.to!int;
 | 
			
		||||
 | 
			
		||||
                    if ([1, 2, 4].canFind(flag))
 | 
			
		||||
                        return flag;
 | 
			
		||||
 | 
			
		||||
                    log.c("В конфигурационном файле не верны настройки web-host.logoutput");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                })
 | 
			
		||||
                .fold!((a, b) => a | b);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.c("В конфигурационном файле не верны настройки web-host.logoutput");
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (wh.logoutput & 4) {
 | 
			
		||||
            if (webHost["logfile"].empty) {
 | 
			
		||||
                log.c("В конфигурационном файле не верны настройки web-host.logfile");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                wh.logfile = webHost["logfile"];
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                log.c("В конфигурационном файле не верны настройки web-host.logfile");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return wh;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								source/data.d
									
										
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								source/pgdb.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
module pgdb;
 | 
			
		||||
 | 
			
		||||
import arsd.postgres;
 | 
			
		||||
import std.stdio;
 | 
			
		||||
 | 
			
		||||
alias pgsql = PG.getConnection;
 | 
			
		||||
 | 
			
		||||
class PG : PostgreSql {
 | 
			
		||||
private:
 | 
			
		||||
    static PG _pgsql;
 | 
			
		||||
    this(string config) {
 | 
			
		||||
        super(config);
 | 
			
		||||
    }
 | 
			
		||||
public:
 | 
			
		||||
    @property static PG getConnection(string config = null) {
 | 
			
		||||
        if (this._pgsql is null) {
 | 
			
		||||
            try {
 | 
			
		||||
                this._pgsql = new PG(config);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                throw new Exception(e.msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._pgsql;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PostgresResult sql(T...)(string query, T t) {
 | 
			
		||||
        return cast(PostgresResult)this.query(query, t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void commit() {
 | 
			
		||||
        this.query("COMMIT");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void rollback() {
 | 
			
		||||
        this.query("ROLLBACK");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								source/requests/groupnumbers.d
									
										
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								source/requests/listsgroups.d
									
										
									
									
									
										Normal 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());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								source/response.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
module response;
 | 
			
		||||
 | 
			
		||||
import vibe.vibe;
 | 
			
		||||
 | 
			
		||||
struct Res {
 | 
			
		||||
    bool error;
 | 
			
		||||
    string message;
 | 
			
		||||
    string data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void send(HTTPServerResponse res, bool error = false, string message = "", string data = "") {
 | 
			
		||||
    res.writeJsonBody(Res(
 | 
			
		||||
        error, message, data
 | 
			
		||||
    ).serializeToJson());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								source/structures.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
module structures;
 | 
			
		||||
 | 
			
		||||
struct ServerInfo {
 | 
			
		||||
    string name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct WebHost {
 | 
			
		||||
    string[] addresses;
 | 
			
		||||
    ushort http = 0;
 | 
			
		||||
    ushort https = 0;
 | 
			
		||||
    string cert;
 | 
			
		||||
    string key;
 | 
			
		||||
    string data;
 | 
			
		||||
    string title;
 | 
			
		||||
    int loglevel = -1;
 | 
			
		||||
    int logoutput = 0;
 | 
			
		||||
    string logfile;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct GroupDB {
 | 
			
		||||
    string name;
 | 
			
		||||
    string comment;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct NumberDB {
 | 
			
		||||
    string number;
 | 
			
		||||
    string group;
 | 
			
		||||
    string list;
 | 
			
		||||
    int all_cc;
 | 
			
		||||
    int white_cc;
 | 
			
		||||
    int black_cc;
 | 
			
		||||
    string comment;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								source/verinfo.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
module verinfo;
 | 
			
		||||
 | 
			
		||||
import std.algorithm: startsWith;
 | 
			
		||||
import version_;
 | 
			
		||||
import std.array : split, join;
 | 
			
		||||
 | 
			
		||||
string getDasterVersion() {
 | 
			
		||||
	auto verstr = dasterVersion;
 | 
			
		||||
	if (verstr.startsWith("v"))
 | 
			
		||||
        verstr = verstr[1 .. $];
 | 
			
		||||
	auto parts = verstr.split("-");
 | 
			
		||||
	if (parts.length >= 3) {
 | 
			
		||||
		if (parts[$-1].length == 8 && parts[$-1][1 .. $].isHexNumber() && parts[$-2].isNumber())
 | 
			
		||||
			verstr = parts[0 .. $-2].join("-") ~ "+" ~ parts[$-2 .. $].join("-");
 | 
			
		||||
	}
 | 
			
		||||
	return verstr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private bool isHexNumber(string str) {
 | 
			
		||||
	foreach (ch; str)
 | 
			
		||||
		switch (ch) {
 | 
			
		||||
			case '0': .. case '9': break;
 | 
			
		||||
			case 'a': .. case 'f': break;
 | 
			
		||||
			case 'A': .. case 'F': break;
 | 
			
		||||
			default: return false;
 | 
			
		||||
		}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private bool isNumber(string str) {
 | 
			
		||||
	foreach (ch; str)
 | 
			
		||||
		switch (ch) {
 | 
			
		||||
			case '0': .. case '9': break;
 | 
			
		||||
			default: return false;
 | 
			
		||||
		}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								source/version_.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
module version_;
 | 
			
		||||
 | 
			
		||||
enum dasterVersion = "v0.0.1";
 | 
			
		||||
							
								
								
									
										22
									
								
								views/group-numbers-list.dt
									
										
									
									
									
										Normal 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}
 | 
			
		||||
							
								
								
									
										39
									
								
								views/index.dt
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
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='style.css')
 | 
			
		||||
    script(src='jquery-3.7.0.min.js')
 | 
			
		||||
    script(src='jquery-ui.min.js')
 | 
			
		||||
    script(src='message.js')
 | 
			
		||||
    script(src='script.js')
 | 
			
		||||
body
 | 
			
		||||
    div.div-header
 | 
			
		||||
        // div.div-add
 | 
			
		||||
        //     button.addNumber Добавить номер
 | 
			
		||||
        div.div-search
 | 
			
		||||
            input.input-focus.search(name='search', type='text', value='', placeholder='Найти номер')
 | 
			
		||||
        // div.div-user Вы вошли как #{user.name}
 | 
			
		||||
        div.div-user Вы вошли как Александр
 | 
			
		||||
        div.div-button
 | 
			
		||||
            button Выход
 | 
			
		||||
            // button(onclick='logout()') Выход
 | 
			
		||||
    div.content
 | 
			
		||||
        div#tabs
 | 
			
		||||
            ul
 | 
			
		||||
                li
 | 
			
		||||
                    a(href='#tabs-numbers') Номера телефонов
 | 
			
		||||
                li
 | 
			
		||||
                    a(href='#tabs-sms') SMS
 | 
			
		||||
                li
 | 
			
		||||
                    a(href='#tabs-ussd') USSD
 | 
			
		||||
                li
 | 
			
		||||
                    a(href='#tabs-server') Сервер
 | 
			
		||||
            div#tabs-numbers
 | 
			
		||||
            div#tabs-sms
 | 
			
		||||
                p Список SMS сообщений
 | 
			
		||||
            div#tabs-ussd
 | 
			
		||||
                p Список результатов USSD запросов
 | 
			
		||||
            div#tabs-server
 | 
			
		||||
                p Информация о сервере
 | 
			
		||||