Пояснительная записка к электронному журналу с использованием технологии Блокчейн, практическая часть: часть 3
Продолжаем.
2.2.3 PHP, часть 1
Эта часть находится в подпапке web директории проекта и имеет структуру:
add (папка с html файлами форм добавления):
1.1. assessment.html – добавление оценки
1.2. disciple.html — ученика
1.3. lactor.html — преподавателя
1.4. lesson.html — предмета
1.5. lesson_topics.html — тем предмета.
Будет рассматриваться код одного файла, т. к. он везде идентичен
(отличаются только поля).
Js
2.1. helper.js — JS код веб-части;
2.2. jquery.min.js — jQuery (стандартная библиотека — рассматривать не
будем);
2.3. sjcl.min.js — jQuery библиотека шифрования и расшифровки данных.
Также код файла рассматривать не будем (её методы используются в
helper.js).
Add.php – файл добавления данных в блокчейн;
db.php — подключение к базе данных и функции работы с ней;
functions.php — файл функций (функция главной страницы, функция базы
данных и пр.)
index.php — основной файл: вызов функции homePage из functions.php и
вывод данных в html коде, вызов по Ajax op.php с контентом;
op.php — файл, вызываемый по Ajax. Получает данные: пользователя и
значение page, после чего производит проверку на соответствие
авторизовавшегося одному из пользователей и выводит данные в
зависимости от значения page;
style.css — файл стилей;
1. Папка add
Формы сгенерированы при помощи моего сервиса
https://viz.dpos.space/custom.
Код формы из файла assessment.html
<form id="istr-i52" class="generated-form">
<p style="display: none;">
<label>
Таблица<br>
<input type="text" name="table" value="assessments"> <br>
</label>
</p>
<fieldset>
<legend>Данные</legend>
<p>
<label>
Дата (формат: 20191605(<br>
<input type="text" name="date" value=""> <br>
</label>
</p>
<p>
<label>
Предмет<br>
<input type="text" name="lesson" value=""> <br>
</label>
</p>
<p>
<label>
Ученик<br>
<input type="text" name="disciple" value=""> <br>
</label>
</p>
<p>
<label>
Оценка<br>
<input type="text" name="data[assessment]" value=""> <br>
</label>
</p>
</fieldset>
<p><button>Отправить</button></p>
</form>
<script>!function(){function
e(e){if(localStorage.getItem("login")&&localStorage.getItem("PostingKey"))viz_login=localStorage.getItem("login"),posting_key=sjcl.decrypt(viz_login+"_postingKey",localStorage.getItem("PostingKey"));else
if(sessionStorage.getItem("login")&&sessionStorage.getItem("PostingKey"))viz_login=sessionStorage.getItem("login"),posting_key=sjcl.decrypt(viz_login+"_postingKey",sessionStorage.getItem("PostingKey"));else{document.getElementById(e)&&(document.getElementById(e).style.display="none");var
t=document.createElement("div");t.innerHTML='<form id="auth_form"
action="index.html" method="GET"><p
class="auth_title"><strong>Пожалуйста
авторизируйтесь</strong></p><p><input type="text"
id="this_login" name="viz_login" placeholder="Ваш
логин"></p><p><input type="password" name="posting"
id="this_posting" placeholder="Приватный постинг
ключ"></p><p><input type="submit"
value="Войти"></p></form>',document.getElementById(e).parentNode.insertAdjacentElement("beforeend",t),found"):(t=e[o],viz.config.set("websocket",t),viz.api.getDynamicGlobalPropertiesAsync().then(e=>{console.log("found
working
node",t),localStorage.setItem("node",t)}).catch(e=>{console.log("connection
error",t,e),n(o+1)}))};n(o)}(),e(t),document.querySelector(".generated-form").querySelector("button").disabled=!1):(console.log("wait"),setTimeout(o,50))},0);document.querySelector(".generated-form").onsubmit=function(e){e.preventDefault(),this.querySelector("button").disabled=!0;var
t=new
XMLHttpRequest;t.open("POST","https://viz.dpos.space/custom/json_encode.php"),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onload=function(){200===t.status?(console.log(t.responseText),viz.broadcast.custom(posting_key,[],[viz_login],document.querySelector(".generated-form").id,t.responseText,function(e,t){e?alert("Ошибка:
"+e):(alert("Ок. custom
отправлен"),console.log(t)),docum
ent.querySelector(".generated-form").querySelector("button").disabled=!1})):alert("Request
failed. Returned status of "+t.status)},t.send(function(e){for(var
t=[],o=0;o<e.elements.length;o++){ return
t.join("&")}(document.querySelector(".generated-form")))}}()</script>
Поля добавления оценки: первое в скрытом абзаце — название таблицы,
дата, предмет, ученик, оценка.
Далее идёт js код, который вызывает форму авторизации, если не
авторизован, который обращается к ajax скрипту на
viz.dpos.space/custom, который принимает данные и вызывает скрипт
отправки данных в блокчейн.
Функция checkWorkingNode
Выбирает паблик-Ноду блокчейна, к которой подключиться:
function checkWorkingNode() {
const NODES = [
"wss://solox.world/ws",
"wss://vizlite.lexai.host/",
];
let node = localStorage.getItem("node") || NODES[0];
const idx = Math.max(NODES.indexOf(node), 0);
let checked = 0;
const find = (idx) 😕> {
if (idx >= NODES.length) {
idx = 0;
}
if (checked >= NODES.length) {
alert("no working nodes found");
return;
}
node = NODES[idx];
console.log("check", idx, node);
viz.config.set("websocket", node);
try {
viz.api.stop();
} catch(e) {
}
let timeout = false;
let timer = setTimeout(() 😕> {
console.log("timeout", NODES[idx])
timeout = true;
find(idx + 1);
}, 3000);
viz.api.getDynamicGlobalPropertiesAsync()
.then(props 😕> {
if(!timeout) {
check = props.head_block_number;
console.log("found working node", node);
localStorage.setItem("node", node);
clearTimeout(timer);
}
})
.catch(e 😕> {
console.log("connection error", node, e);
find(idx + 1);
});
}
find(idx);
}
checkWorkingNode();
Проверяет работоспособность текущей паблик-Ноды, беря её из
localStorage, используя getDynamicGlobalPropertiesAsync (Если возвращает
ошибку или пустое значение, значит не всё ок). Если ошибка, проверяет
паблик-Ноды из списка массива nodes. Когда находится работающая,
выбирается и добавляется в localStorage.
Ниже создаются переменные
var viz_login = ''; // Логин
var posting_key = ''; // Регулярный (ранее постинг) ключ
Функция getUrlVars
Берёт из url get запросы, позволяет с ними работать.
function getUrlVars() {
var vars = {};
var parts =
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,
function(m,key,value) {
vars[key] = value;
});
return vars;
}
Async функция ajaxSend
Отправляет ajax запрос:
async function ajaxSend(login) {
if (getUrlVars()) { // Проверка наличия get параметров
var data = getUrlVars(); // Назначение массива параметров переменной
data
page = data['page'].toLowerCase(); // Назначение переменной page GET
параметра соответствующего с переводом в нижний регистр
action = (data['action'] !== undefined) ? "&action=" +
data["action"] : ''; // Проверка на наличие GET action и добавление
значения или, если его нет, создание пустой переменной.
lactor = (data['lactor'] !== undefined) ? "&lactor=" +
data["lactor"] : ''; // Проверка наличия значения GET lactor.
Используется при удалении.
disciple = (data['disciple'] !== undefined) ? "&disciple=" +
data["disciple"] : ''; // Проверка disciple с возвратом значения, если
есть.
lesson = (data['lesson'] !== undefined) ? "&lesson=" +
data["lesson"] : '';// Также с lesson .
$(document).ready(function() { // jQuery: Вызывается при готовности
документа.
$(".content").load("op.php", "user=" + login + "&page=" + page + action
- lactor + disciple + lesson); // Отправка ajax запроса со всеми
возможными переменными.
});
}
}
Функция async userAuth
async function userAuth() {
let login = $('#this_login').val(); // Получаем логин из поля
let posting = $('#this_posting').val(); // Также и ключ
if (localStorage.getItem('PostingKey')) { // Если есть ключ в
localStorage
var isPostingKey = sjcl.decrypt(login + '_postingKey',
localStorage.getItem('PostingKey')); // Расшифровываем ключ, используя
логин, слово _postingKey и зашифрованный ключ из localStorage.
} else if (sessionStorage.getItem('PostingKey')) { // Если кл
юч в
sessionStorage
var isPostingKey = sjcl.decrypt(login + '_postingKey',
sessionStorage.getItem('PostingKey')); // Также расшифровываем, но берём
из сессий.
} else { // Иначе
var isPostingKey = posting; // Берём значение из переменной
}
var resultIsPostingWif = viz.auth.isWif(isPostingKey); // Проверяем, что
ключ валиден.
if (resultIsPostingWif === true) { // Если это так
const account_approve = await viz.api.getAccountsAsync([login]); //
вызываем информацию о пользователе
const public_wif = viz.auth.wifToPublic(isPostingKey); // Приобразуем
полученный приватный ключ в публичный
let posting_public_keys = []; // Создаём массив публичных ключей
if (account_approve.length > 0) { // Если данные аккаунта есть
for (key of account_approve[0].regular_authority.key_auths) { //
Проходим по массиву ключей публичных
posting_public_keys.push(key[0]); // Добавляем их в массив
posting_public_keys.
}
} else {
window.alert('Вероятно, аккаунт не существует. Просьба проверить
введённый логин.'); // Иначе, если данных аккаунта нет, вывести
это сообщение.
}
if (posting_public_keys.includes(public_wif)) { // Далее проверяем,
есть ли наш публичный ключ в массиве posting_public_keys. Если
находит, делаются дальнейшие действия.
var isSavePosting = document.getElementById('isSavePosting'); // Смотрит
элемент с чекбоксом в форме авторизации
if (isSavePosting.checked) { // Если отмечен
localStorage.setItem('login', login); // Добавляем логин в localStorage
localStorage.setItem('PostingKey', sjcl.encrypt(login + '_postingKey',
posting)); // и в зашифрованном виде постинг ключ.
} else { // Если не отмечен.
sessionStorage.setItem('login', login); // добавляется логин в Сессии
sessionStorage.setItem('PostingKey', sjcl.encrypt(login +
'_postingKey', posting)); // ч.
}
viz_login = login; // Добавляется логин в переменную
posting_key = isPostingKey; // и ключ.
} else {
window.alert('Постинг ключ не соответствует пренадлежащему аккаунту.');
// Если в массиве ключей нашего public нет, выводится это сообщение.
}
} else {
window.alert('Постинг ключ имеет неверный формат. Пожалуйста, попробуйте
ещё раз.'); // Если формат неверен, выводится такое сообщение (относится
к resultIsPostingWif).
}
if (!viz_login && !posting_key) { // Если нет значений у переменных
логина и ключа
$('#delete_posting_key').css("display", "none"); // Скрывается блок с
кнопкой выхода.
$('#unblock_form').css("display", "block"); // Отображается блок с
формой входа.
} else {
$('#unblock_form').css("display", "none"); // иначе скрывается форма
входа
$('#delete_posting_key').css("display", "block"); // а ссылка выхода
отображается.
jQuery("#delete_posting_key").html('<p align="center"><a
onclick="localStorage.removeItem(\'login\'\);
localStorage.removeItem(\'PostingKey\'\);
location.reload();">Выйти</a></p>');// Также выводится в
соответствующем элементе ссылка выхода, удаляющая из localStorage
логин и ключ.
ajaxSend(localStorage.getItem('login')); // и вызывается функция
отправки ajax.
}
} // end userAuth
Всё
Благодарю за внимание. С вами был незрячий автор, программист и делегат @denis-skripnik. До встречи в новых постах.