<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Javascript on Шлакоблог</title><link>https://vodolaz095.ru/tags/javascript/</link><description>Recent content in Javascript on Шлакоблог</description><generator>Hugo -- 0.162.1</generator><language>ru-RU</language><lastBuildDate>Fri, 09 Jan 2026 00:13:42 +0300</lastBuildDate><atom:link href="https://vodolaz095.ru/tags/javascript/index.xml" rel="self" type="application/rss+xml"/><item><title>Генератор QR кодов</title><link>https://vodolaz095.ru/qrcode/</link><pubDate>Fri, 09 Jan 2026 00:13:42 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/qrcode/</guid><description>&amp;lt;no value&amp;gt;</description><content type="text/html" mode="escaped"><![CDATA[<p><a href="https://ru.wikipedia.org/wiki/QR-%D0%BA%D0%BE%D0%B4">QR код</a> позволяет закодировать в абстрактном изображении
почти любой текст. Большинство смартфонов могут декодировать и прочитать QR код с помощью камеры.
Например, QR код можно нанести на визитку.</p>
<p>Примеры текста, из которого можно сгенерировать QR код:</p>
<table>
	<thead>
			<tr>
					<th>Тип</th>
					<th>Пример</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Просто текст</td>
					<td><code>Просто текст</code></td>
			</tr>
			<tr>
					<td>Телефон</td>
					<td><code>tel:+79012345678</code></td>
			</tr>
			<tr>
					<td>Email</td>
					<td><code>mailto:blog@example.org</code></td>
			</tr>
			<tr>
					<td>Email с темой и текстом</td>
					<td><code>mailto:blog@example.org?subject=something&amp;body=lalala</code></td>
			</tr>
			<tr>
					<td>SMS</td>
					<td><code>SMSTO:+79012345678:</code></td>
			</tr>
			<tr>
					<td>Ссылка на сайт</td>
					<td><code>https://vodolaz095.ru/</code></td>
			</tr>
			<tr>
					<td>Настройки WIFI (шифрование - WPA/WPA2)</td>
					<td><code>WIFI:S:my_wifi;T:WPA;P:12345678;;</code></td>
			</tr>
	</tbody>
</table>
<p>На этой странице есть простой генератор QR кодов, с помощью которого вы можете нагенерировать свои коды.
Код библиотеки генерации QR кода взят отсюда: <a href="https://github.com/davidshimjs/qrcodejs">https://github.com/davidshimjs/qrcodejs</a>.</p>
<form id="form_qr">
<p><label for="text">Введите текст для QR кода:</label></p>
<p><input style="color: #1e0010;width:100%" id="text" type="text" placeholder="https://vodolaz095.ru/" value="https://vodolaz095.ru/"/></p>
<p>
    <input style="color:#1e0010;width:100%" type="submit" value="Сгенерировать QR code">
    <input style="color:red;width:100%" type="reset" value="Cбросить">
</p>
</form>
<div id="qrcode3" style="width:300px; height:300px; margin-top:15px;"></div>
<script src="/qrcode/qrcode.min.js"></script>
<script type="text/javascript">
const elText = document.getElementById("text");
const qrcode = new QRCode(document.getElementById("qrcode3"), {width : 300, height : 300});
const qrForm = document.getElementById("form_qr");
qrForm.addEventListener('submit', function(event) {
    event.preventDefault();
        if (!elText.value) {
            alert("Введите текст для генерации QR кода!");
            elText.focus();
            return false;
        }
    qrcode.makeCode(elText.value);
    return false;
});
</script>
]]></content></item><item><title>systemd - как работать с NodeJS сервисом, у которого утекает память?</title><link>https://vodolaz095.ru/systemd-memleaky-nodejs/</link><pubDate>Thu, 18 Feb 2021 18:26:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/systemd-memleaky-nodejs/</guid><description>В статье рассмотрен один из вариантов борьбы с утечками памяти в приложении на NodeJS, которые начинаются после нескольких часов работы</description><enclosure url="https://vodolaz095.ru/images/systemd_memleaky_node.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/systemd_memleaky_node.png" width="1073" height="736"/><media:content url="https://vodolaz095.ru/images/systemd_memleaky_node.png" medium="image" type="image/png" width="1073" height="736"><media:title type="html">/images/systemd_memleaky_node.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/systemd_memleaky_node.png" alt="systemd_memleaky_node.png"></p>
<p>Всем хороша среда выполнения Javascript NodeJS, но у неё есть три недостатка.</p>
<ul>
<li>Первый недостаток - много низкокачественных NPM модулей, включая те, в которых происходят утечки памяти.</li>
<li>Второй недостаток - обычная программа требует множество модулей, и обязательно среди них будет несколько, в которых утекает память.</li>
<li>Третий недостаток - иногда утечки памяти начинаются после часов, а то и суток работы программы.</li>
</ul>
<p>Что с этим делать?
Можно потратить много времени, разобраться, где утекает память, написать исправление к модулю и послать Pull-Request разработчикам модуля,
который они скорее всего проигнорируют, так как у них проблема не воспроизводится, или же они, как обычно, не видят в этом проблему.
А можно просто перезапустить программу через несколько часов работы, когда начинают появляться утечки памяти.</p>
<p>Для примера, рассмотрим почтовый сервер на основе <a href="https://haraka.github.io/">https://haraka.github.io/</a>, который, после запуска
потребляет 56.8 мегабайт памяти, а через несколько суток - уже 180 мегабайт.</p>
<p>Данный сервер запускается с помощью systemd, используя этот unit файл:</p>
<pre tabindex="0"><code>[Unit]
Description=Haraka inbound STMP server
After=network.target

[Service]
Type=simple

User=haraka
Group=haraka

WorkingDirectory=/opt/haraka/haraka-inbound
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=1s
Environment=NODE_ENV=production

# Hardening
ProtectHome=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
</code></pre><p>Как его можно перезапустить, допустим, раз в час, используя systemd?</p>
<p>Для этого достаточно почитать документацию к systemd, и найти этот параметр конфигурации:</p>
<p><a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#RuntimeMaxSec">https://www.freedesktop.org/software/systemd/man/systemd.service.html#RuntimeMaxSec</a></p>
<pre tabindex="0"><code>RuntimeMaxSec=

Configures a maximum time for the service to run. If this is used 
and the service has been active for longer than the specified time it is terminated 
and put into a failure state. Note that this setting does not have any effect 
on Type=oneshot services, as they terminate immediately after activation 
completed. Pass &#34;infinity&#34; (the default) to configure no runtime limit.
</code></pre><p>То есть, задав этот параметр равным 3600, мы добьёмся того, что systemd будет автоматически останавливать, и
снова запускать почтовый сервер каждый час. Так как Haraka написан неожиданно хорошо для nodejs приложений,
он не только быстро перезагружается, он ещё знает, как закончить все транзакции перед перезапуском, и поэтому
ни одно письмо не пропадёт.</p>
<p>Такой unit файл был в итоге создан для почтового сервера:</p>
<pre tabindex="0"><code>
[Unit]
Description=Haraka inbound STMP server
After=network.target

[Service]
Type=simple

User=haraka
Group=haraka

WorkingDirectory=/opt/haraka/haraka-inbound
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=1s
Environment=NODE_ENV=production

# Gracefully restart service every hour to prevent memleaks
RuntimeMaxSec=3600

# Hardening
ProtectHome=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
</code></pre><p>Теперь почтовый сервер перезапускается раз в час, и в среднем потребляет около 60 мегабайт памяти.</p>
]]></content></item><item><title>NodeJS и ldapJS - как авторизовать пользователя и прочитать свой профиль из базы данных OpenLDAP?</title><link>https://vodolaz095.ru/nodejs-openldap/</link><pubDate>Tue, 16 Feb 2021 19:14:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/nodejs-openldap/</guid><description>Задача состояла в том, что пользователь вводит логин (в виде user1) и пароль в NodeJS приложении. И программа должна проверить, угадал ли пользователь пароль, а потом программа должна загрузить все данные о пользователе из базы данных OpenLDAP.</description><enclosure url="https://vodolaz095.ru/images/ldap_idm_integration.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/ldap_idm_integration.png" width="500" height="500"/><media:content url="https://vodolaz095.ru/images/ldap_idm_integration.png" medium="image" type="image/png" width="500" height="500"><media:title type="html">/images/ldap_idm_integration.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/ldap_idm_integration.png" alt="ldap_idm_integration.png"></p>
<p>Недавно на работе у меня было такое задание - соединить NodeJS приложение с OpenLDAP сервером, обладающем следующей структурой данных:
в корне - структура Organization Unit с профилями пользователями, доступная, для примера, по DN
<code>ou=people,dc=vodolaz095,dc=ru</code> и имеющая следующую структуру:</p>
<pre tabindex="0"><code>dn: ou=people,dc=vodolaz095,dc=ru
objectclass: organizationalUnit
ou: people
</code></pre><p>В этой структуре - уже были профили пользователей - например, <code>uid=user1,ou=people,dc=vodolaz095,dc=ru</code>, <code>uid=user2,ou=people,dc=vodolaz095,dc=ru</code> и т.д.</p>
<p>Аккаунты пользователей принадлежали к типам <code>posixAccount</code>, <code>inetOrgPerson</code>, <code>organizationalPerson</code>, <code>person</code>.</p>
<p>Аккаунты пользователей выглядели примерно вот так:</p>
<pre tabindex="0"><code>dn: uid=user1,ou=people,dc=vodolaz095,dc=ru
cn: Jane Doe
gidnumber: 1000
givenname:  Jane
homedirectory: /home/janedoe
initials: JS
loginshell: /bin/bash
objectclass: posixAccount
objectclass: inetOrgPerson
objectclass: organizationalPerson
objectclass: person
mail: user1@vodolaz095.ru
sn: Doe
uid: user1
uidnumber: 1002
userpassword: {SSHA}HF3O9bpD+/a5iswMxRCwBgboZgV2Mkpp
</code></pre><p>При этом для каждого пользователя был задан пароль.</p>
<p>Задача состояла в том, что пользователь вводит логин (в виде <code>user1</code>) и пароль в NodeJS приложении.
И программа должна проверить, угадал ли пользователь пароль, а потом программа должна загрузить все
данные о пользователе из базы данных OpenLDAP.</p>
<p>Сначала я попробовал данный метод авторизации, который, к сожалению, требует наличия пользователя
<code>readonly</code> с полным доступом на чтение ко всей базе данных, что, по моему, не очень
безопасно - так как данный пользователь может прочитать все данные, включая те,
которые могут составлять коммерческую или же медицинскую тайну.
Также пароль пользователя <code>readonly</code> может измениться, и приложение перестанет работать.</p>
<p>Для работы по протоколу openLDAP я использовал следующую NodeJS библиотеку - <a href="https://www.npmjs.com/package/ldapjs">https://www.npmjs.com/package/ldapjs</a>.
Минимальный работающий образец исходного кода представлен тут:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldap</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ldapjs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldapClient</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ldap</span>.<span style="color:#a6e22e">createClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">url</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ldap://127.0.0.1:389&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;cn=readonly,dc=vodolaz095,dc=ru&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">password</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;readonly&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">bind</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">username</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">password</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;bind performed&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">search</span>(<span style="color:#e6db74">&#39;ou=people,dc=vodolaz095,dc=ru&#39;</span>, {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">filter</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`(uid=vodolaz095)`</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">scope</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;one&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;uid&#39;</span>, <span style="color:#e6db74">&#39;dn&#39;</span>, <span style="color:#e6db74">&#39;cn&#39;</span>, <span style="color:#e6db74">&#39;mail&#39;</span>]
</span></span><span style="display:flex;"><span>    }, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">res</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;searchEntry&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">data</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// console.log(&#39;Data found&#39;, data);
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Data object&#39;</span>, <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>(<span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">object</span>, <span style="color:#66d9ef">null</span>, <span style="color:#ae81ff">2</span>));
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;error&#39;</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">error</span>){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;end&#39;</span>, <span style="color:#66d9ef">function</span> () {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Completed&#39;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>Как видно из кода, мы сначала &ldquo;биндимся&rdquo; к базе данных как пользователь <code>readonly</code> с
DN <code>cn=readonly,dc=vodolaz095,dc=ru</code>, потом ищем пользователя в коллекции
<code>ou=people,dc=vodolaz095,dc=ru</code> по фильтру <code>(uid=vodolaz095)</code>, и если мы его нашли,
то можно попробовать &ldquo;забиндиться&rdquo; как этот пользователь к базе данных, тем самым проверив пароль, подходит ли он.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldap</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ldapjs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldapClient</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ldap</span>.<span style="color:#a6e22e">createClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">url</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ldap://127.0.0.1:389&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">password</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;superSecretPasswordOfVodolaz095!&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#a6e22e">username</span>, <span style="color:#a6e22e">password</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;пароль подошёл, пользователь vodolaz095 авторизирован!&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p><strong>Можно ли как то упростить данную процедуру?</strong></p>
<p>Возможно ли не использовать пользователя <code>readonly</code>?</p>
<p>Можно ли минимизировать число запросов данных к базе данных ldap?</p>
<p>Ответ:</p>
<p><strong>Да, можно</strong></p>
<p>И сейчас я расскажу, как.
Оказывается, большинство openLDAP серверов позволяют авторизоваться (&ldquo;забиндиться&rdquo;) как искомый пользователь, и даже получить его профиль.</p>
<p>Рассмотрим данный код:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldap</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ldapjs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ldapClient</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ldap</span>.<span style="color:#a6e22e">createClient</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">url</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;ldap://127.0.0.1:389&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">username</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">password</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;thisIsVerySecureSecretPassword&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">bind</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">username</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">password</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;bind performed&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ldapClient</span>.<span style="color:#a6e22e">search</span>(<span style="color:#e6db74">&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;</span>, { 
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">scope</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;base&#39;</span>, <span style="color:#75715e">// important
</span></span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;uid&#39;</span>, <span style="color:#e6db74">&#39;dn&#39;</span>, <span style="color:#e6db74">&#39;cn&#39;</span>, <span style="color:#e6db74">&#39;mail&#39;</span>]
</span></span><span style="display:flex;"><span>    }, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">res</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;searchEntry&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">data</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// console.log(&#39;Data found&#39;, data);
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Data object&#39;</span>, <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>(<span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">object</span>, <span style="color:#66d9ef">null</span>, <span style="color:#ae81ff">2</span>));
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;error&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">once</span>(<span style="color:#e6db74">&#39;end&#39;</span>, <span style="color:#66d9ef">function</span> () {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;All passed&#39;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>С помощью этого кода мы напрямую пытаемся &ldquo;забиндиться&rdquo; к базе данных ldap как ограниченный пользователь, используя функцию
<code>ldapClient.bind(username,  password, function(error){....})</code>.</p>
<p>Если &ldquo;забиндиться&rdquo; получилось, то это означает, что пароль подошёл. Теперь надо получить профиль пользователя, и по умолчанию,
все пользователи ldap имеют права на чтение своего собственного профиля.
По крайней мере, так работает в контейнеризированной сборке openLDAP из <a href="https://github.com/osixia/docker-openldap">https://github.com/osixia/docker-openldap</a>.</p>
<p>После того, как мы забиндились, мы можем попробовать найти свой собственный профиль. Это можно сделать с помощью данной функции:</p>
<pre tabindex="0"><code>    ldapClient.search(&#39;uid=vodolaz095,ou=people,dc=vodolaz095,dc=ru&#39;, { 
      scope: &#39;base&#39;, 
      attributes: [&#39;uid&#39;, &#39;dn&#39;, &#39;cn&#39;, &#39;mail&#39;]
    }, function (error, res) {....})
</code></pre><p>В итоге, мы получаем всю информацию из профиля пользователя в базе данных openldap,
при этом приложение использует только пароль, который предоставил сам пользователь,
доступ на чтение ко всей базе данных не нужен.</p>
]]></content></item><item><title>Как бороться с ошибкой read ECONNRESET в Ghost</title><link>https://vodolaz095.ru/fix-econnreset-in-ghost/</link><pubDate>Sun, 16 Jun 2019 15:22:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/fix-econnreset-in-ghost/</guid><description>Иногда мой блог вместо страницы выдавал ошибку 500, с отображением SQL запроса, который не прошёл. select count(distinct posts.id) as aggregate from posts where (posts.status = &amp;#39;published&amp;#39; and posts.page = false)</description><enclosure url="https://vodolaz095.ru/images/fix_econnreset_ghost.png" length="" type="image/png"/><media:thumbnail url="https://vodolaz095.ru/images/fix_econnreset_ghost.png" width="1075" height="717"/><media:content url="https://vodolaz095.ru/images/fix_econnreset_ghost.png" medium="image" type="image/png" width="1075" height="717"><media:title type="html">/images/fix_econnreset_ghost.png</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p>Иногда мой блог вместо страницы выдавал ошибку 500, с отображением SQL запроса, который не прошёл.
Это выглядело так:</p>
<p><img src="/images/fix_econnreset_ghost.png" alt="fix_econnreset_ghost.png"></p>
<p>Код ошибки мог меняться в замисимости от страницы, но почти всегда это был SQL запрос вроде такого</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#66d9ef">count</span>(<span style="color:#66d9ef">distinct</span> posts.id) <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">aggregate</span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">from</span> posts 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">where</span> (posts.status <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;published&#39;</span> <span style="color:#66d9ef">and</span> posts.page <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>)
</span></span></code></pre></div><p>Главное, что в нём всегда встречалось <code>read ECONNRESET</code>.</p>
<p>Причина неполадок состояла в том, что сервер базы данных закрывал соединение с блог платформой, так как оно,
по мнению сервера, не использовалось.
Данная неполадка чинится отправкой этих запросов на MariaDB сервер через консоль сервера MySQL/MariaDB:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> <span style="color:#66d9ef">GLOBAL</span> connect_timeout<span style="color:#f92672">=</span><span style="color:#ae81ff">28800</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> <span style="color:#66d9ef">GLOBAL</span> wait_timeout<span style="color:#f92672">=</span><span style="color:#ae81ff">28800</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> <span style="color:#66d9ef">GLOBAL</span> interactive_timeout<span style="color:#f92672">=</span><span style="color:#ae81ff">28800</span>;
</span></span></code></pre></div><p>Эти переменные задают критерии, используемые сервером для того, чтобы определить, какие соединения считаются неиспользуемыми,
и их можно разорвать. Я поставил значение в 8 часов (28800 секунд), и разрывы соединения пропали.
Подробнее о всех системных переменных базы данных <a href="https://mariadb.com/kb/ru/library/server-system-variables/">MariaDB</a>.</p>
<h1 id="обновление-1">Обновление 1.<a href="#%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-1" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>При перезагрузке сервера с базой данных, глобальные переменные к сожалению сбрасываются.
Одно из возможных решений проблемы ошибки с разрывом соединения было описано в этом комментарии
<a href="https://github.com/TryGhost/Ghost/issues/9739#issuecomment-406240883">https://github.com/TryGhost/Ghost/issues/9739#issuecomment-406240883</a>
Пользователь с ником <em>Toub</em> посоветовал периодически загружать главную страницу блога,
чтобы платформа посылала запросы к базе данных, и соединение восстанавливалось.</p>
<p>При этом техническая поддержка платформы Ghost показала особенную клиентоориентированность.
Z понял, что они ничем помочь не могут и не хотят, по их мнению, официальные образы докера с
неправильно работающим соединением с базой данных это нормально.
Поэтому, проблему я начал решать сам.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;use strict&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">async</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;async&#39;</span>); <span style="color:#75715e">// tested with  version &gt;=2.6.2
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">request</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;request&#39;</span>); <span style="color:#75715e">//  tested with version &gt;=2.88.0
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">async</span>.<span style="color:#a6e22e">doWhilst</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">callback</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">request</span>(<span style="color:#e6db74">&#39;https://blog.vodolaz095.life&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">response</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">callback</span>(<span style="color:#a6e22e">error</span>);
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">i</span> <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Test %s. Status code - %s&#39;</span>, <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">statusCode</span>); <span style="color:#75715e">// eslint-disable-line
</span></span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">callback</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">statusCode</span>);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">statusCode</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">statusCode</span> <span style="color:#f92672">!==</span> <span style="color:#ae81ff">200</span>;
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">throw</span> <span style="color:#a6e22e">error</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;Blog awaken!&#39;</span>);  <span style="color:#75715e">// eslint-disable-line
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>И поставил его в крон на запуск каждую минуту.</p>
<pre tabindex="0"><code>* * * * * cd /home/vodolaz095/ &amp;&amp; node /home/vodolaz095/wakeBlog.js
</code></pre><p>Посмотрим, что из этого получится&hellip;</p>
<h1 id="обновление-2">Обновление 2<a href="#%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-2" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Скрипт для побудки блога проработал несколько лет. Но в 2026 году я решил делать личный сайт на <a href="https://gohugo.io/">Hugo</a>.
Поэтому, можно считать, что проблему я решил.</p>
]]></content></item><item><title>Документация о Javascript</title><link>https://vodolaz095.ru/javascript-links/</link><pubDate>Sat, 08 Jun 2019 02:06:27 +0300</pubDate><author>Анатолий Остроумов</author><guid>https://vodolaz095.ru/javascript-links/</guid><description>Полезные ссылки на документацию по языку программирования Javascript</description><enclosure url="https://vodolaz095.ru/images/autumn_railway.jpg" length="" type="image/jpeg"/><media:thumbnail url="https://vodolaz095.ru/images/autumn_railway.jpg" width="900" height="600"/><media:content url="https://vodolaz095.ru/images/autumn_railway.jpg" medium="image" type="image/jpeg" width="900" height="600"><media:title type="html">/images/autumn_railway.jpg</media:title></media:content><content type="text/html" mode="escaped"><![CDATA[<p><img src="/images/autumn_railway.jpg" alt="autumn_railway.jpg"></p>
<p>Полезные ссылки на документацию по языку программирования Javascript:</p>
<ol>
<li><a href="https://developer.mozilla.org/ru/">https://developer.mozilla.org/ru/</a></li>
<li><a href="https://javascript.ru">https://javascript.ru</a></li>
<li><a href="https://nodejs.org/api/">https://nodejs.org/api/</a></li>
</ol>
]]></content></item></channel></rss>