Внутренний веб-сервер::Журнал СА 10.2004
www.samag.ru
Льготная подписка для студентов      
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

Jobsora

ЭКСПЕРТНАЯ СЕССИЯ 2019


  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
28.05.2019г.
Просмотров: 1826
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 1887
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 1446
Комментарии: 0
Django 2 в примерах

 Читать далее...

28.05.2019г.
Просмотров: 1066
Комментарии: 0
Введение в анализ алгоритмов

 Читать далее...

27.03.2019г.
Просмотров: 1636
Комментарии: 1
Arduino Uno и Raspberry Pi 3: от схемотехники к интернету вещей

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Внутренний веб-сервер

Архив номеров / 2004 / Выпуск №10 (23) / Внутренний веб-сервер

Рубрика: Администрирование /  Сайт

СЕРГЕЙ СУПРУНОВ

Внутренний веб-сервер

Что админу хорошо, то пользователю – смерть.

 

Народная мудрость

Очень часто системному администратору, работающему в небольшой фирме, попутно приходится разрабатывать программы для внутренних нужд компании. Требования к подобному ПО, как правило, невысоки, но это с лихвой компенсируется очень сжатыми сроками, отводимыми на разработку. Освоить Delphi для того, чтобы на предприятии появилась программа-каталог пользователей Интернета или программа, формирующая материальные отчеты, было бы просто замечательно. Но времени на подобные вещи, как обычно, не хватает. И потому приходится идти другим путём. В данной статье я хочу рассказать о своем опыте использования веб-сервера для решения подобных задач.

Почему был выбран столь экзотический способ? Во-первых, Perl я знаю несколько лучше, чем Delphi или C++ Builder. Во-вторых, такой подход без лишних усилий позволяет создавать клиент-серверные приложения, с которыми одновременно могут работать сотни пользователей, не заботясь о разработке клиентских программ – с этой ролью отлично справится любой браузер. В-третьих, налицо независимость от конкретных платформ и операционных систем – для сервера достаточно, чтобы на нем мог работать Apache + Perl (вы можете использовать и другую связку), от клиентов требуется лишь поддержка какого-нибудь графического обозревателя. И наконец, времени на разработку и сопровождение ПО в данном случае затрачивается заметно меньше, чем при «традиционных» способах.

Для наглядности рассмотрим в общих чертах процесс разработки незатейливого приложения: каталога пользователей ADSL, в который будут заноситься сведения об абонентах (фамилия, адрес, номер телефона), параметры конфигурации (IP-адреса, интерфейсы, номера PVC), параметры линии (длина, диаметр жилы, сопротивление шлейфа) и т. д.

В целях экономии места будет рассмотрена только первая функция (работа со сведениями об абонентах). Очень многие детали придется опустить. Так, не будут рассмотрены особенности Perl-модулей, подключаемых к нашим сценариям, способы работы с базой данных и т. д. Читателю понадобятся, по крайней мере, базовые знания Perl, HTML, PostgreSQL (или какой-нибудь другой СУБД). Если материал статьи окажется вам интересен, я постараюсь разложить все по полочкам в следующих статьях, оставляйте ваши отзывы на форуме журнала «Системный администратор».

Подготовительные мероприятия

Итак, прежде всего нам нужно собрать сервер. Я остановил свой выбор (поскольку все это уже есть и работает) на следующем ПО (правда, кое-что из этого списка уже пора бы обновить):

  • ОС: FreeBSD 5.2
  • Веб-сервер: Russian Apache 1.3.29
  • Язык программирования: Perl 5.6.1
  • СУБД: PostgreSQL 7.4.2

Рассматривать установку и конфигурирование всего этого я не буду – все довольно подробно описано и на страницах журнала, и на бескрайних просторах Интернета. Выбор именно этого ПО – вопрос, скорее, личных предпочтений, поскольку по каждому из пунктов можно привести массу как положительных, так и отрицательных доводов. В конце концов с тем же успехом (с поправкой на более высокие требования к ресурсам и вопросам безопасности) можно использовать и связку «Windows2003 – IIS – ASP – MSSQL».

Структура базы данных и доступ к СУБД

Начнем разработку с определения структуры БД. Нужно заметить, что это итерационный процесс, то есть обычно при разработке сценариев выясняется, что база данных должна быть несколько иной, потом корректировки вновь вносятся в код сценариев, снова исправляется БД и так до тех пор, пока разработчик не осознает, что эффект от дальнейшего улучшения уже не окупает затрат на исправления. Но мы остановимся на первой итерации, тем более что наша задача – показать сам принцип.

Итак, создадим БД с именем adsl, владельцем которой будет пользователь adsluser с паролем password. В ней нам потребуются следующие таблицы:

  • sessions – информация сеансов пользователей (см. далее):
  • id char(32) – идентификатор сессии;
  • a_session text – информация сессии;
  • login char(12) – имя пользователя;
  • password varchar – пароль пользователя.
  • users – информация об абонентах ADSL:
  • uid serial – уникальный идентификатор абонента;
  • name varchar – фамилия, имя, отчество;
  • address varchar – адрес проживания;
  • phone char(7) – номер телефона.
  • dslam– информация о ADSL-портах:
  • uid serial – уникальный идентификатор порта;
  • userid numeric – идентификатор подключенного на порт абонента;
  • num char(10) – номер порта (в виде nDSLAM/nBOARD/nPORT);
  • vlan numeric(4) – номер VLAN, соответствующей ADSL-порту;
  • vpi numeric(3) – номер VPI;
  • vci numeric(3) – номер VCI, присвоенный клиенту;
  • interface char(15) – имя интерфейса, на котором будет вестись учет трафика;
  • ipaddress inet – IP-адрес, сопоставленный с данным портом.
  • lines – характеристики линий связи:
  • uid serial – уникальный идентификатор линии;
  • portid numeric – идентификатор порта DSLAM, на который подключена эта линия;
  • length numeric(5) – длина линии в метрах;
  • diameter numeric(2,1) – диаметр жилы в миллиметрах;
  • impedance numeric(4) – сопротивление шлейфа в Омах.

И еще одна таблица для хранения служебной информации:

  • st_modules – список функциональных модулей:
  • name char(20) – имя модуля;
  • description varchar – описание модуля;
  • ink varchar – ссылка на сценарий модуля;
  • allow char(12)[] – массив, хранящий имена пользователей, которым позволено работать с данным модулем;
  • orderby numeric(2) – данное поле задает порядок вывода модулей на экран.

В данном случае мы минимально задействуем расширенные возможности PostgreSQL, что позволит почти ничего не менять при использовании, например, MySQL.

Шаблон сайта – модульный подход

Поскольку переписывать все сначала при необходимости расширить функциональность нашего приложения – занятие не очень интересное, применим модульный подход. Пусть основной сценарий отвечает только за предоставление доступа к имеющимся функциям, а каждая функция будет реализована отдельным скриптом. Кроме того, часто используемые операции будем выносить в наш модуль My::Insite.

Выглядеть базовый сценарий будет примерно так:

#!/usr/bin/perl –w

#-------------------------------------------- adsl.cgi

use My::Insite;

# Подключаемся к БД и создаем объект CGI для работы с HTTP

$dbh = My::Insite->DBConnect("adsl", "adsluser", "password");

$cgi = My::Insite->CGIStart();

# Считываем значение HTTP-параметра «action»

($action = $cgi->param("action")) or $action = "";

# Выполняем процедуру выхода

if($action eq "logoff") { &doLogoff; }

# Процедура авторизации

if($action eq "logon") {

    $savedLogin    = $cgi->param("login");

    $savedPassword = $cgi->param("password");

# ищем сессию для заявленного логина

    ($sSessId, $sPassword) = $dbh->selectrow_array("

           SELECT id, password FROM sessions WHERE login=?

                               ", undef, $savedLogin);

# если не нашли – повторный запрос авторизации

    if(!$sSessId) { &doLogon("Failed"); }

# если сессия есть, но пароль не соответствует введенному, повторный запрос авторизации

    if($sPassword ne $savedPassword) { &doLogon("Wrong"); }

# Если все нормально – сохраняем идентификатор сессии в cookie

    $cookie = $cgi->cookie(-name => "sessid", -value => $sSessId);

    print $cgi->header(-cookie => $cookie);

    print "Авторизация выполнена успешно.";

    print " <A href="adsl.cgi">Продолжить...</A>";

    exit;

}

# action не имеет значения, пытаемся извлечь из cookie идентификатор сессии

$sessId = $cgi->cookie("sessid");

# Если безуспешно – уходим на авторизацию

if(!$sessId) { &doLogon("First"); }

# Если sessId есть, пытаемся получить пользователя этой сессии

($sLogin) = $dbh->selectrow_array("

    SELECT login FROM sessions WHERE id=?;

           ", undef, $sessId);

# Если удачно – открываем сессию, иначе – на авторизацию

if($sLogin) {

    $session = My::Insite->SessOpen($dbh, $sessId);

} else { &doLogon("Fialed"); }

# Выбираем из БД и выводим на экран список модулей

print $cgi->header;

$sth = $dbh->prepare("SELECT * FROM st_modules ORDER BY orderby;");

$sth->execute;

print "<P align="right">Вы вошли под именем $sLogin | ";

print " <A href="?action=logoff">Выход</A></P>";

while($rhash = $sth->fetchrow_hashref) {

# Печатать будем только те модули, для которых в поле allow есть имя вошедшего пользователя

    if($$rhash{allow} =~ m($sLogin)) {

           print "<DT><A href="$$rhash{link}"> $$rhash{name}</A>";

           print "<DD>$$rhash{description}<BR>";

    }

}

# Все закрываем (в принципе это не обязательно – все и так закроется)

My::Insite->SessClose($session);

My::Insite->DBDisconnect($dbh);

exit;

#-------------------------------------------- подпрограммы

sub doLogon {  # подпрограмма авторизации

    $status = shift @_;

    if($status eq "Wrong") {

           $status = Неправильный логин или пароль.";

    } elsif($status eq "Failed") {

           $status = Ошибка подключения данного пользователя.";

    } else {

           $status = Введите логин и пароль:";

    }

    print $cgi->header();

    print <<__HTML__;

<CENTER><H3>$status</H3><FORM method="POST">

<TABLE border="1"><TR><TD><TABLE>

<INPUT type="hidden" name="action" value="logon">

<TR><TD>Login:<TD><INPUT type="text" name="login" value="">

<TR><TD>Password:

    <TD><INPUT type="password" name="password" value="">

<TR><TD colspan="2" align="center">

    <INPUT type="submit" value="Войти">

</TABLE></TABLE></FORM></CENTER>

__HTML__

    exit;

}

sub doLogoff {  # подпрограмма закрытия сеанса

# Записываем cookie с истекшим «сроком годности» (отрицательное значение параметра expire), что уничтожит cookie в памяти

    $cookie = $cgi->cookie(-name   => "sessid",

                         -value   => "",

                         -expires => "-1d");

    print $cgi->header(-cookie => $cookie);

    print "<HEADER>";

    print "<META http-equiv="refresh" content="1;url=adsl.cgi">";

    print "</HEADER>";

    print "До новых встреч!";

    exit;

}

Задача данного сценария – выполнить авторизацию пользователя и предоставить ему список доступных для работы модулей. Управление поведением сценария осуществляется с помощью переменной «action», которая может иметь одно из следующих значений: logoff (закрыть сеанс), logon (выполнить процедуру авторизации, в ходе которой проверяется правильность пароля и открывается сессия, соответствующая данному пользователю, о чем делается запись в файлах cookie). Пустое значение данной переменной позволит вывести на экран перечень доступных модулей.

Список модулей хранится в БД, в таблице st_modules, откуда он выбирается и выводится на экран, причем отображаются только те модули, для которых в поле allow содержится имя текущего пользователя. Больше ничего от главного сценария не требуется. Для подключения к приложению очередного модуля достаточно поместить в папку cgi-bin реализующий его сценарий и добавить запись в таблицу st_modules. То есть в этой таблице будет что-то похожее:

adsl=> select link, name, allow from st_modules order by orderby;

      link       |          name             |       allow             

---------------------+------------------------------------------+----------------------

 adsl-users.cgi      | Абоненты ADSL            | {"admin","operator"}

 adsl-dslam.cgi      | Конфигурация DSLAM       | {"admin"}

 adsl-admin.cgi      | Модуль администратора      | {"admin"}

(записей: 3)

Результат работы сценария adsl.cgi представлен на рисунках 1 и 2.

Рисунок 1

Рисунок 1

Рисунок 2

Рисунок 2

Модуль My::Insite в моем случае будет размещаться по такому адресу: /usr/local/lib/perl5/site_perl/5.6.1/My/Insite.pm. Узнать пути, по которым Perl ищет подключаемые модули, позволяет специальная переменная @INC:

#!/usr/bin/perl

#-------------------- testpath.pl

 

$, = " ";

print @INC;

exit;

На моей машине результат был получен следующий:

$ ./testpath.pl

/usr/local/lib/perl5/site_perl/5.6.1/mach

/usr/local/lib/perl5/site_perl/5.6.1

/usr/local/lib/perl5/site_perl

/usr/local/lib/perl5/5.6.1/BSDPAN

/usr/local/lib/perl5/5.6.1/mach

/usr/local/lib/perl5/5.6.1

Код модуля My::Insite представлен ниже:

package My::Insite;

use CGI;

use DBI;

use Apache::Session::Postgres;

sub CGIStart {

    return CGI->new;

}

sub DBConnect {

    my($obj, $dbName, $dbUser, $dbPwd) = @_;

    return DBI->connect("dbi:Pg:dbname=".$dbName, $dbUser, $dbPwd);

}

sub DBDisconnect {

    my($obj, $dbh) = @_;

    $dbh->disconnect;

    return(1);

}

sub SessOpen {

    my($obj, $dbh, $sessId) = @_;

    tie %session, "Apache::Session::Postgres", $sessId,

                 {Handle => $dbh, LockHandle => $dbh};

    return(bless(\%session, $obj));

}

sub SessClose {

    my($obj, $session) = @_;

    untie(%$session);

    return(1);

}

return(1);

Как видите, сюда вынесены функции подключения к БД, работы с сессиями и т. д.

Может показаться, что в некоторых функциях нет смысла. Например, зачем создавать CGIStart, которая только и делает, что вызывает функцию new() модуля CGI? Не проще ли вызывать эту функцию самому и не захламлять модуль?

А теперь представьте, что вы решили вместо модуля CGI перейти на более функциональный. Что проще – переписывать все имеющиеся сценарии или изменить одну функцию в My::Insite?

Думаю, в этом модуле все понятно без комментариев. Если что непонятно – всегда под рукой man DBI, man CGI, man Apache::Session.

Доступ на сайт и Apache::Session

Если вы доверяете всем сотрудникам или собираетесь ограничивать доступ к сайту «низкоуровневыми» средствами вроде брандмауэра для ограниченного круга лиц с равными правами, то этот пункт можно пропустить. В общем же случае желательно организовать проверку «подлинности» пользователя и соответствующим образом ограничивать его права в нашей программе. В серьезных случаях можно дополнительно организовать SSL-шифрование, но сейчас обойдемся без этого, чтобы не отвлекаться от основной цели.

Пароли для простоты хранить и передавать будем в явном виде, признак правильного входа в приложение, а заодно и некоторые персональные настройки будем хранить, используя механизм сессий. В Perl это выглядит несколько сложнее, чем в PHP, зато проще сделать именно то, что нужно. Для работы понадобится модуль Apache::Session. Если на вашей системе такого нет, для FreeBSD его, как и большинство других модулей, можно установить из коллекции портов:

# cd /usr/ports/www/p5-Apache-Session

# make install

Более универсальный путь, пригодный практически для всех систем – использование архива CPAN. Этот метод описан на страницах руководства man perlmodinstall.

Так как база данных у нас есть, целесообразно для хранения сессионной информации использовать именно ее. Поэтому будем использовать подмодуль Apache::Session:: Postgres. Поскольку число пользователей нашего приложения ограничено и все они известны, то имеет смысл для каждого из них заранее создать сессию, в которой будут храниться все пользовательские данные, и при авторизации подключать именно ее. Такой подход позволит не беспокоиться об удалении старых сессий и о хранении идентификатора сессии между сеансами. Вручную создавать новую сессию не очень удобно, поэтому будем использовать такой небольшой сценарий:

#!/usr/bin/perl –w

#------------------------------------------ adsl-adduser.pl

use DBI;

use Apache::Session::Postgres;

$login    = $ARGV[0];   # первый аргумент – имя

$password = $ARGV[1];   # второй – пароль

# Запрашиваем все, что не передано в аргументах

if(!$login) {

    print "Enter login: ";

    chomp($login = <>);

}

if(!$password) {

    print "Enter password: ";

    chomp($password = <>);

}

# Создаем новую сессию и сразу закрываем

$dbh = DBI->connect("dbi:Pg:dbname=adsl", "adsluser", "password");

tie %session, "Apache::Session::Postgres", undef,

               {Handle => $dbh, LockHandle => $dbh};

untie %session;

# В запись в таблице sessions, соответствующей нашему сеансу, добавляем имя пользователя и пароль, введенные выше

$pre = $dbh->prepare("

                 update sessions

                    set login = ?, password = ?

                    where login is null;

                    ");

$pre->execute($login, $password);

$dbh->disconnect;

exit;

При желании этот модуль можно сделать CGI-скриптом и организовать доступ к нему через модуль администрирования, подключаемый к нашему приложению, как и все остальные.

Ну и раз информация сессий будет необходима нам в каждом модуле, то процедуры работы с ней вынесены в наш модуль My::Insite. Как все это будет работать – смотрите в листингах, приведенных в статье.

Взаимодействие с БД

Для работы с базой данных будем использовать Perl-модуль DBI с драйвером DBD::Pg. Данный модуль и нужный драйвер можно установить как из портов, так и из CPAN. Функции открытия и закрытия соединения вынесены в модуль My::Insite, остальное смотрите в коде конкретных модулей.

Модуль обработки информации об абоненте

Вот мы и добрались до первого «рабочего» модуля. В его рамках нам нужно решить следующие задачи: вывод на экран списка абонентов, ввод нового абонента, удаление абонента, изменение данных.

Код модуля следующий:

#!/usr/bin/perl –w

#------------------------------------ adsl-users.cgi

use My::Insite;

$dbh = My::Insite->DBConnect('adsl', 'adsluser', 'password');

$cgi = My::Insite->CGIStart();

$action = $cgi->param('action');

# Пытаемся считать из cookie идентификатор сессии, если безуспешно – отправляем на авторизацию

$sessId = $cgi->cookie('sessid');

if(!$sessId) {

    &toLogon;

}

# Открываем сессию, или на авторизацию в случае ошибки

($sLogin) = $dbh->selectrow_array('

    SELECT login FROM sessions WHERE id=?;

                        ', undef, $sessId);

if($sLogin) {

    $session = My::Insite->SessOpen($dbh, $sessId);

} else {

    &toLogon;

}

# Проверяем, можно ли данному пользователю работать с этим модулем

($allow) = $dbh->selectrow_array('

        SELECT allow FROM st_modules WHERE link=?;',

            undef, 'adsl-users.cgi');

if($allow !~ m($sLogin)) {

    &toLogon;

}

print $cgi->header;

# Разбираем возможные действия

if   ($action eq ''          ) { &showUsers;  }

elsif($action eq 'user'      ) { &userForm;   }

elsif($action eq 'changeuser') { &changeUser; }

else { print 'Не могу выполнить: '.$action; }

My::Insite->SessClose($session);

My::Insite->DBDisconnect($dbh);

exit;

#------------------------------------- subroutines

sub showUsers {  # подпрограмма вывода списка абонентов

    $sth = $dbh->prepare('SELECT uid, name, address, phone

                          FROM users ORDER BY name;');

    $sth->execute;

    print '<TABLE><TR><TD><H3>Абоненты</H3>';

    print '<TD align="right"><A href="adsl.cgi">Главная</A>';

    print '<TR><TD colspan="2">';

    print '<TABLE><TR><TD align="right">';

    print '[ <A href="?action=user&type=add">

                 Добавить нового абонента</A> ]';

    print '<TR><TD><TABLE border=1><TR bgcolor=#AAAAFF>

                               <TH>Абонент

                               <TH>Адрес

                               <TH>Телефон

                               <TH>Действие;

    while(@res = $sth->fetchrow_array) {

       $oper = ($res[6] eq 'I'?'inlager':'outlager');

       print "<TR><TD>$res[1]<TD>$res[2]<TD>$res[3]<TD>

           [ <A href='?action=user&type=update&uid=$res[0] '>Изменить</A> ] ::

           [ <A href='?action=user&type=delete&uid=$res[0] '>Удалить</A> ]";

    }

    print '</TABLE><TR><TD align="right">';

    print '[ <A href="?action=user&type=add">Добавить нового абонента</A> ]';

    print '</TABLE></TABLE>';

    return;

}

sub userForm {  # выводит форму для манипуляций с данными

    $uid = $cgi->param('uid');

    $type = $cgi->param('type');

    if($type eq 'add') { $submitName = 'Добавить'; }

    elsif($type eq 'delete') { $submitName = 'Удалить'; }

    elsif($type eq 'update') { $submitName = 'Изменить'; }

    else {

           print 'Ошибка операции: '.$type;

           exit;

    }

    $header = $submitName.' абонента:';

    if($type ne 'add') {

        ($name, $address, $phone) = $dbh->selectrow_array('

        SELECT name, address, phone FROM users WHERE uid = ?;

           ', undef, $uid);

    } else {

       $name = $address = $phone = '';

    }

    if($type eq 'delete') {

       $in1 = "<B>$name</B>";

       $in2 = "<B>$address</B>";

       $in3 = "<B>$phone</B>";

    } else {

        $in1 = "<INPUT type='text' name='name' value='$name' size='35'>";

      $in2 = "<INPUT type='text' name='address' value='$address' size='35'>";

      $in3 = "<INPUT type='text' name='phone' value='$phone' size='7'>";

    }

    print <<__HTML__;

<CENTER><H3>$header</H3>

<FORM method="GET">

<TABLE border="1"><TR><TD><TABLE>

<INPUT type="hidden" name="action" value="changeuser">

<INPUT type="hidden" name="type" value="$type">

<INPUT type="hidden" name="uid" value="$uid">

<TR><TD>Абонент: <TD>$in1

<TR><TD>Адрес:   <TD>$in2

<TR><TD>Телефон: <TD>$in3

<TR><TD colspan="2"><HR>

<TR><TD>[ <A href="adsl-users.cgi?action=">Отмена</A> ]

    <TD align="right"><INPUT type="submit" value="$submitName">

</TABLE></TABLE></FORM></CENTER>

__HTML__

    exit;

}

sub changeUser {  # запись изменений в БД

    $type = $cgi->param('type');

    $uid = $cgi->param('uid');

    $name = $cgi->param('name');

    $address = $cgi->param('address');

    $phone = $cgi->param('phone');

    if($type eq 'add') {

        $res = $dbh->do('

        INSERT INTO users(name, address, phone)

                 VALUES(?, ?, ?);',

        undef, $name, $address, $phone);

    } elsif($type eq 'delete') {

        $res = $dbh->do('DELETE FROM users WHERE uid=?;', undef, $uid);

    } elsif($type eq 'update') {

        $res = $dbh->do('

        UPDATE users SET name=?, address=?, phone=? WHERE uid=?;

           ', undef, $name, $address, $phone, $uid);

    } else { print 'Ошибка операции: '.$type; }  

    if($res) { print 'Операция выполнена успешно. '};

    print '<BR><A href="?action=">Продолжить...</A>';

    exit;

}

sub toLogon {

    print $cgi->header;

    print '<META http-equiv="refresh" content="1;url=adsl.cgi?account=logon">';

    print 'Ошибка входа. Перенаправление...';

    exit;

}

В данном случае для управления поведением сценария используется еще одна переменная – type. Если action определяет, на какую подпрограмму следует передавать управление, то type содержит информацию о том, что именно следует делать в данной подпрограмме.

Сгенерированный приложением список абонентов имеет вид, представленный на рисунке 3. Рисунок 4 демонстрирует форму для изменения данных.

Рисунок 3

Рисунок 3

Рисунок 4

Рисунок 4

Прочие модули

Поскольку другие модули ничего нового и интересного в себе не содержат, отличаясь от приведенного лишь именами и количеством полей, а также некоторыми интерфейсными особенностями, то и тратить время на их рассмотрение не будем. Добавив соответствующую запись в таблицу st_modules, вы сделаете новый модуль доступным для работы.

Что можно изменить?

Как известно, нет предела совершенству. Рассмотренный здесь пример был очень сильно урезан и упрощен, чтобы за деталями не потерялась суть и чтобы уложиться в рамки журнальной статьи. Однако, разрабатывая реальное приложение, имеет смысл сделать некоторые улучшения.

Например, «шаблонность» нашего приложения оставляет желать лучшего. Каждый добавляемый модуль в принципе имеет очень схожую структуру и функциональность. То есть можно разработать один модуль-шаблон и настраивать его под конкретные таблицы и поля автоматически в процессе обращения к конкретной функции.

Можно сделать более гибкой систему разграничения доступа, помимо пользователей введя понятие групп пользователей, а также разграничивая права пользователей в пределах одного модуля (полный доступ, только чтение).

Механизм сессий используется очень слабо. Например, его можно использовать для передачи таких параметров как идентификатор пользователя (uid), вместо того чтобы делать это с помощью скрытых полей формы. Не совсем удобной выглядит необходимость в каждом модуле задавать логин и пароль для подключения к БД. Выносить это в модуль My::Insite неправильно (иначе будут сложности с использованием данного модуля в других приложениях для подключения к другим базам), а вот сделать что-то типа конфигурационного файла и брать нужные данные оттуда было бы намного лучше, поскольку в случае смены имени или пароля корректировка потребуется только в одном месте. В существенном улучшении нуждается проверка корректности вводимых данных, контроль ошибок, и т. д. В реальной жизни этим, конечно же, пренебрегать нельзя.

И вообще, можно сделать более удобный и красивый дизайн, добавить страничкам «динамизм» с помощью JavaScript (например, всплывающие подсказки, предупреждения и т. п.) и много еще чего хорошего и полезного.

Заключение

Ну что ж. Надеюсь, полученный результат хотя бы частично соответствует нашим ожиданиям, несмотря на множество недоработок, оставленных «за бортом». Мы получили гибкое, легко модифицируемое приложение, соответствующее большинству наших требований. В будущем его без труда можно расширить, добавив, например, модуль для работы с жалобами абонентов, для сбора статистики по потребленному трафику и оплатам и т. д. Единственное, чего мне в данный момент не хватает, это красивых отчетов, которые не стыдно было бы распечатать, сохранить в файл, отправить по электронной почте. Эта задача тоже решается довольно просто. Но об этом – в следующей статье.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru