123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /**
- * АРХИВ ПУБЛИКАЦИЙ ОБУЧАЮЩИХСЯ И СОТРУДНИКОВ СУНЦ УрФУ
- * Copyright © 2021, А.М.Гольдин. ISC license
- */
- "use strict";
- /* ПОДКЛЮЧЕНИЕ МОДУЛЕЙ И ОПРЕДЕЛЕНИЕ ГЛОБАЛЬНЫХ КОНСТАНТ
- * ----------------------------------------------------------------------- */
- const http = require("http"),
- nedb = require("@yetzt/nedb"),
- fetch = require("node-fetch"),
- api = require("./api"),
- sertGen = require("./api/sertGen"),
- putFile = require("./api/putFile"),
- {
- ADMIN, admEml, SALT, authServ, director, glred,
- smtpSrv, smtpPort, smtpUs, smtpPwd
- } = require("./config");
- global.fs = require("fs");
- global.sendEml = require("./api/sendEml");
- global.ADMIN = ADMIN;
- global.ADMEML = admEml;
- global.DIRECTOR = director;
- global.GLRED = glred;
- global.SMTPSRV = smtpSrv;
- global.SMTPPORT = smtpPort;
- global.SMTPUS = smtpUs;
- global.SMTPPWD = smtpPwd;
- const mmmagic = require("mmmagic"),
- magic = mmmagic.Magic;
- global.mType = new magic(mmmagic.MAGIC_MIME_TYPE);
- /* ИНИЦИАЛИЗАЦИЯ КОЛЛЕКЦИЙ БАЗЫ ДАННЫХ
- * ----------------------------------------------------------------------- */
- global.db = {};
- db.articles = new nedb({filename:`${__dirname}/db/articles.db`, autoload:true});
- db.staff = new nedb({filename:`${__dirname}/db/staff.db`, autoload:true});
- /* ОПРЕДЕЛЕНИЯ ФУНКЦИЙ
- * ----------------------------------------------------------------------- */
- // Промисификатор метода find() работы с базой db
- // Пример вызова: let res = await dbFind("curric", {type: "class"})
- global.dbFind = (collectionName, objFind) => {
- return new Promise((resolve, reject) => {
- db[collectionName].find(objFind, (err, docs) => {
- if (err) reject(err);
- else resolve(docs);
- })
- })
- };
- // Изготавление хэша длины 24 из строки str
- const hash = str => {
- let
- alph = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
- char, strNew, h = 0, pass = '';
- for (let j = 0; j < 24; j++) {
- strNew = j + str;
- for (let i = 0; i < strNew.length; i++) {
- char = strNew.charCodeAt(i);
- h = ((h << 5) - h) + char;
- }
- pass += alph[Math.abs(h) % alph.length];
- }
- return pass;
- }
- // Отправка ответа (otvet - объект ответа, content - тело)
- const sendOtvet = (otvet, content) => {
- otvet.writeHead(200, {"Content-Type": "text/plain"});
- otvet.end(content);
- }
- /* СОБСТВЕННО ЦИКЛ ОБРАБОТКИ ЗАПРОСА
- * ----------------------------------------------------------------------- */
- http.createServer(async (zapros, otvet) => {
- try {
- // Количество дней, прошедших с начала Юникс-эры
- let DT = ~~(Date.now()/(1000 * 3600 * 24));
-
- // Получаем параметры запроса
- let ADDR = zapros.socket.remoteAddress || "unknown";
- ADDR = ADDR.replace("::1", "127.0.0.1").replace(/\:.*\:/, '');
- let url = new URL("http://" + zapros.headers.host + zapros.url);
- let qstring = (url.search || '').trim(); // вместе с ?;
- let cookies = {}, cookiesStr = zapros.headers.cookie || '';
- let cookiesArr = cookiesStr.split(';').map(x => {
- let keyValueArr = x.trim().split('=');
- cookies[keyValueArr[0]] = decodeURIComponent(keyValueArr[1]);
- });
- // Если пришел запрос на поиск (без авторизации)
- if (qstring == "?find") {
- let postData = '';
- zapros.on("data", dann => postData += dann.toString());
- zapros.on("end", async () => {
- let cont = await api(postData, [0, 'a', 'a', 'a', '']);
- sendOtvet(otvet, cont);
- });
- }
- // Первичная авторизация (логин-пароль передается в query string)
- else if (qstring.includes('-')) {
- // От сервера авторизации приходит либо none, либо
- // "pupkin,Пупкин,Василий,Петрович,"
- let resp = "none";
- try {
- resp = await (await fetch(`${authServ + qstring}`)).text();
- } catch (e) {;}
- if (resp == "none") sendOtvet(otvet, "noauth");
- else {
- // Определяем роли этого клиента (Editor, Corrector, Reviewer)
- // присобачиваем в качестве первой цифры куки (двоичная маска)
- let login = resp.split(',')[0], roles = 0;
- if (login == ADMIN) roles = 8;
- else {
- let res = await dbFind("staff", {login: login});
- if (res.length) roles = res[0].roles;
- }
- // Готовим куку u=4pupkin,Пупкин,Василий,Петрович,jgkjhs6
- let token = hash(SALT + ADDR + DT + roles + resp),
- cook = `u=${encodeURIComponent(roles+resp+token)}; path=/`;
- otvet.writeHead(200, {
- "Content-Type": "text/plain", "Set-Cookie": cook});
- otvet.end("auth");
- }
- }
- // Обработка запроса к API (кроме первичной авторизации)
- else {
- // Объект с данными юзера (роли, логин, фам, имя, отч (возм., пустое))
- let user = [];
- // Проверяем, авторизован ли клиент
- let auth = false;
- if (cookies['u']) {
- let [lgRl, uFam, uName, uOtch, uToken] = cookies['u'].split(','),
- tokTrue = hash(SALT + ADDR + DT
- + [lgRl, uFam, uName, uOtch].join(',') + ',');
- if (uToken == tokTrue) {
- auth = true;
- let uRoles = Number(lgRl[0]), uLgn = lgRl.slice(1);
- user = [uRoles, uLgn, uFam, uName, uOtch];
- }
- }
- if (!auth) sendOtvet(otvet, "forbidden");
- // Если пришел запрос на добавление файла <host>/api/?put_<id>
- else if (/^\?put_/.test(qstring)) {
- let id = qstring.replace("?put_", ''),
- fl = Buffer.alloc(0);
- zapros.on("data", dann => fl = Buffer.concat([fl, dann]));
- zapros.on("end", async () => {
- let cont = await putFile(user, id, fl);
- sendOtvet(otvet, cont);
- });
- }
- // Если пришел запрос изображения <host>/api/?draft_<id>/<flName>
- else if (/^\?draft_\w+\/\d+\.(jpg|png|svg)$/.test(qstring)) {
- const TYPES = {
- "jpg":"image/jpeg", "png":"image/png", "svg":"image/svg+xml"
- };
- let fullName = qstring.replace("?draft_", ''),
- ext = fullName.split('.')[1]; ;
- fs.readFile(__dirname + `/draft/${fullName}`, (e, data) => {
- if(e) {
- otvet.writeHead(404, {"Content-Type": "text/plain"});
- otvet.end("404 Not found");
- }
- else {
- otvet.writeHead(200, {"Content-Type": TYPES[ext]});
- otvet.end(data);
- }
- });
- }
- // Если пришел запрос просмотра черновика <host>/api/?view_<id>
- else if (/^\?view_\w+$/.test(qstring)) {
- let id = qstring.replace("?view_", ''); ;
- let cont = await api(
- JSON.stringify({f:"loadArticle", dt:id}),
- user
- );
- otvet.writeHead(200, {"Content-Type": "text/html"});
- otvet.end(cont);
- }
- // Если пришел запрос сертификата статьи вида <host>/api/?fjhgdj
- else if (/^\?\w{3,20}$/.test(qstring))
- sertGen(otvet, qstring.slice(1), user);
- // Если пришел запрос к api
- else {
- let postData = '';
- zapros.on("data", dann => postData += dann.toString());
- zapros.on("end", async () => {
- let cont = await api(postData, user);
- sendOtvet(otvet, cont);
- });
- }
- }
- }
- catch(e) {
- otvet.writeHead(500, {"Content-Type": "text/plain"});
- otvet.end("Internal Server Error");
- }
-
- }).listen(8080);
- let now = (new Date()).toString().replace(/ \(.*\)/, '');
- console.info(`${now} lycArch api server стартовал на порту 8080`);
|