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

Пройдите опрос. Монитор технологий. ИИ-блок


  Опросы
  Статьи

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

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

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

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

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

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

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

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

Книжная полка  

От создания сайтов до разработки и реализации API

В издательстве «БХВ» недавно вышли книги, которые будут интересны системным администраторам, создателям

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

Разбор полетов  

Ошибок опыт трудный

Как часто мы легко повторяем, что не надо бояться совершать ошибки, мол,

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

Принципы проектирования  

Dependency Inversion Principle. Принцип инверсии зависимостей в разработке

Мы подошли к последнему принципу проектирования приложений из серии SOLID – Dependency

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

Рынок труда  

Вакансия: Администратор 1С

Администратор 1С – это специалист, который необходим любой организации, где установлены программы

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

Книжная полка  

Книги для профессионалов, студентов и пользователей

Книги издательства «БХВ» вышли книги для тех, кто хочет овладеть самыми востребованными

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

Принципы проектирования  

Interface Segregation Principle. Принцип разделения интерфейсов в проектировании приложений

Эта статья из серии «SOLID» посвящена четвертому принципу проектирования приложений – Interface

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

1001 и 1 книга  
19.03.2018г.
Просмотров: 11081
Комментарии: 0
Потоковая обработка данных

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

19.03.2018г.
Просмотров: 9321
Комментарии: 0
Релевантный поиск с использованием Elasticsearch и Solr

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

19.03.2018г.
Просмотров: 9380
Комментарии: 0
Конкурентное программирование на SCALA

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

19.03.2018г.
Просмотров: 5889
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

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

12.03.2018г.
Просмотров: 6592
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

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

12.03.2018г.
Просмотров: 3899
Комментарии: 0
Глубокое обучение с точки зрения практика

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

12.03.2018г.
Просмотров: 2853
Комментарии: 0
Изучаем pandas

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

12.03.2018г.
Просмотров: 3651
Комментарии: 0
Программирование на языке Rust (Цветное издание)

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

19.12.2017г.
Просмотров: 3654
Комментарии: 0
Глубокое обучение

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

19.12.2017г.
Просмотров: 6139
Комментарии: 0
Анализ социальных медиа на Python

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

Друзья сайта  

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

Архив номеров / 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