From 4c7f79bb7f521045d202e0e6cd7860fdd0fd7b1a Mon Sep 17 00:00:00 2001 From: CCTVcalc Date: Thu, 24 Jul 2025 16:26:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BC=D0=B8=D1=82=20=D0=B4=D0=BB=D1=8F=20gitea?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app.js | 96 -- backend/apps/admin.js | 766 ---------- backend/apps/bot.js | 627 -------- backend/apps/miniapp.js | 643 -------- backend/data/db.sqlite | Bin 94208 -> 0 bytes backend/data/db.sqlite-shm | Bin 32768 -> 0 bytes backend/data/db.sqlite-wal | Bin 28872 -> 0 bytes backend/data/init.sql | 171 --- backend/include/db.js | 57 - backend/package-lock.json | 1990 ------------------------- backend/package.json | 25 - backend/public/index.html | 337 ----- backend/public/miniapp.html | 65 - i18n-2.xlsm | Bin 46547 -> 46580 bytes package-lock.json | 7 - package.json | 1 - quasar.config.ts | 39 +- src/App.vue | 5 +- src/boot/axios.ts | 48 +- src/components/companyInfoPersons.vue | 1 - src/components/meetingBlock.vue | 1 - src/components/pnImageSelector.vue | 2 +- src/components/pnSmallDialog.vue | 96 +- src/components/taskBlock.vue | 2 + src/css/app.scss | 2 +- src/i18n/index.ts | 2 +- src/i18n/ru-RU/index.ts | 2 +- src/pages/MeetingEditPage.vue | 2 +- src/pages/UserInfoPage.vue | 1 - src/pages/main/HeaderPage.vue | 44 +- src/pages/main/TasksPage.vue | 60 +- src/router/index.ts | 14 +- src/stores/auth.ts | 8 + src/stores/projects.ts | 10 +- src/types/Task.ts | 1 + 35 files changed, 168 insertions(+), 4957 deletions(-) delete mode 100644 backend/app.js delete mode 100644 backend/apps/admin.js delete mode 100644 backend/apps/bot.js delete mode 100644 backend/apps/miniapp.js delete mode 100644 backend/data/db.sqlite delete mode 100644 backend/data/db.sqlite-shm delete mode 100644 backend/data/db.sqlite-wal delete mode 100644 backend/data/init.sql delete mode 100644 backend/include/db.js delete mode 100644 backend/package-lock.json delete mode 100644 backend/package.json delete mode 100644 backend/public/index.html delete mode 100644 backend/public/miniapp.html diff --git a/backend/app.js b/backend/app.js deleted file mode 100644 index 8f5d343..0000000 --- a/backend/app.js +++ /dev/null @@ -1,96 +0,0 @@ -const express = require('express') -const bodyParser = require('body-parser') -const cookieParser = require('cookie-parser') -const crypto = require('crypto') -const fs = require('fs') -const util = require('util') -const bot = require('./apps/bot') - -const app = express() - -app.use(bodyParser.json()) -app.use(cookieParser()) - -BigInt.prototype.toJSON = function () { - return Number(this) -} - -app.use((req, res, next) => { - if(!(req.body instanceof Object)) - return next() - - const escapeHtml = str => str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''') - Object - .keys(req.body || {}) - .filter(key => typeof(req.body[key]) == 'string' && key != 'password') - .map(key => req.body[key] = escapeHtml(req.body[key])) - - next() -}) - -// cors -app.use((req, res, next) => { - res.set({ - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE', - 'Access-Control-Allow-Headers': 'Accept,Accept-Language,Content-Language,Content-Type,Authorization,Cookie,X-Requested-With,Origin,Host', - 'Access-Control-Allow-Credentials': true - }) - - return req.method == 'OPTIONS' ? res.status(200).json({success: true}) : next() -}) - -app.post('(/api/admin/customer/login|/api/miniapp/user/login)', (req, res, next) => { - const data = Object.assign({}, req.query) - delete data.hash - const hash = req.query?.hash - - const BOT_TOKEN = '7236504417:AAGVaodw3cRwGlf-jAhwnYb51OHaXcgpW8k' - const dataCheckString = Object.keys(data).sort().map((key) => `${key}=${data[key]}`).join("\n") - const secretKey = crypto.createHmac("sha256", "WebAppData").update(BOT_TOKEN).digest() - const hmac = crypto.createHmac("sha256", secretKey).update(dataCheckString).digest("hex") - - const timeDiff = Date.now() / 1000 - data.auth_date - - if (hmac !== req.query.hash) // || timeDiff > 10) - throw Error('ACCESS_DENIED::401') - - const user = JSON.parse(req.query.user) - res.locals.telegram_id = user.id - res.locals.start_param = req.query.start_param - - if (!res.locals.telegram_id) - throw Error('ACCESS_DENIED::500') - - next() -}) - - -app.use('/api/admin', require('./apps/admin')) -app.use('/api/miniapp', require('./apps/miniapp')) - -app.use((err, req, res, next) => { - console.error(`Error for ${req.path}: ${err}`) - - let message, code - //if (err.code == 'SQLITE_ERROR' || err.code == 'SQLITE_CONSTRAINT_CHECK') { - // message = 'DATABASE_ERROR' - //code = err.code == 'SQLITE_CONSTRAINT_CHECK' ? 400 : 500 - //} else { - [message, code = 500] = err.message.split('::') - //} - - res.status(res.statusCode == 200 ? 500 : res.statusCode).json({success: false, error: { message, code}}) -}) - -app.use(express.static('public')) - -const PORT = process.env.PORT || 3000 -app.listen(PORT, async () => { - console.log(`Listening at port ${PORT}`) - bot.start( - process.env.API_ID || 26746106, - process.env.API_HASH || '29e5f83c04e635fa583721473a6003b5', - process.env.BOT_TOKEN || '7236504417:AAGVaodw3cRwGlf-jAhwnYb51OHaXcgpW8k' - ) -}) \ No newline at end of file diff --git a/backend/apps/admin.js b/backend/apps/admin.js deleted file mode 100644 index 0cb3013..0000000 --- a/backend/apps/admin.js +++ /dev/null @@ -1,766 +0,0 @@ -const crypto = require('crypto') -const express = require('express') -const db = require('../include/db') -const bot = require('./bot') -const fs = require('fs') - -const app = express.Router() - -const sessions = {} -const cache = { - // email -> code - register: {}, - upgrade: {}, - recovery: {}, - 'change-password': {}, - 'change-email': {}, - 'change-email2': {} -} - -function checkEmail(email){ - return String(email) - .toLowerCase() - .match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/) -} - -function sendEmail(email, subject, message) { - console.log(`${email} --> ${subject}: ${message}`) -} - -function checkPassword(password) { - return password.length >= 8 -} - -app.use((req, res, next) => { - const public = [ - '/auth/email', - '/auth/telegram', - '/auth/email/register', - '/auth/email/recovery', - '/auth/logout' - ] - - if (public.includes(req.path)) - return next() - - const asid = req.query.asid || req.cookies.asid - req.session = sessions[asid] - if (!req.session) - throw Error('ACCESS_DENIED::401') - - res.locals.customer_id = req.session.customer_id - next() -}) - -// AUTH -function createSession(req, res, customer_id) { - if (!customer_id) - throw Error('AUTH_ERROR::500') - - res.locals.customer_id = customer_id - const asid = crypto.randomBytes(64).toString('hex') - req.session = sessions[asid] = {asid, customer_id } - res.setHeader('Set-Cookie', [`asid=${asid};httpOnly;path=/api/admin`]) -} - -app.post('/auth/email', (req, res, next) => { - res.locals.email = req.body?.email - res.locals.password = req.body?.password - - const customer_id = db - .prepare(`select id from customers where email = :email and password is not null and password = :password `) - .pluck(true) - .get(res.locals) - - if (!customer_id) - throw Error('AUTH_ERROR::401') - - createSession(req, res, customer_id) - res.status(200).json({success: true}) -}) - -app.post('/auth/telegram', (req, res, next) => { - let customer_id = db - .prepare(`select id from customers where telegram_id = :telegram_id`) - .pluck(true) - .get(res.locals) || db - .prepare(`replace into customers (telegram_id) values (:telegram_id) returning id`) - .pluck(true) - .get(res.locals) - - createSession(req, res, customer_id) - res.status(200).json({success: true}) -}) - -/* - Регистрация нового клиента/Перевод авторизации с TG на email выполняется за ТРИ последовательных вызова - 1. Отравляется email. Если email корректный и уже неиспользуется, то сервер возвращает ОК и на указанный email отправляется код. - 2. Отправляется email + код из письма. Если указан корректный код, то сервер отвечает ОК. - 3. Отправляется email + код из письма + желаемый пароль. Если все ОК, то сервер создает учетную запись и возвращает ОК. -*/ -app.post('/auth/email/:action(register|upgrade)', (req, res, next) => { - const email = String(req.body.email ?? '').trim() - const code = String(req.body.code ?? '').trim() - const password = String(req.body.password ?? '').trim() - const action = req.params.action - - const stepNo = email && !code ? 1 : email && code && !password ? 2 : email && code && password ? 3 : -1 - if (stepNo == -1) - throw Error('BAD_STEP::400') - - if (stepNo == 1) { - if (!checkEmail(email)) - throw Error('INCORRECT_EMAIL::400') - - const customer_id = db - .prepare('select id from customers where email = :email') - .pluck(true) - .get({email}) - - if (customer_id) - throw Error('USED_EMAIL::400') - - const code = Math.random().toString().substr(2, 4) - cache[action][email] = code - sendEmail(email, action.toUpperCase(), `${email} => ${code}`) - } - - if (stepNo == 2) { - if (cache[action][email] != code) - throw Error('INCORRECT_CODE::400') - } - - if (stepNo == 3) { - if (!checkPassword(password)) - throw Error('INCORRECT_PASSWORD::400') - - const query = action == 'register' ? 'insert into customers (email, password) values (:email, :password)' : - 'update customers set email = :email, password = :password where id = :id' - db.prepare(query).run({email, password, id: res.locals.customer_id}) - - delete cache[action][email] - } - - res.status(200).json({success: true}) -}) - - -/* - Смена email выполняется за ЧЕТЫРЕ последовательных вызовов - 1. Отравляется пустой закпрос. Сервер на email пользователя из базы отправляет код. - 2. Отправляется код из письма. Если указан корректный код, то сервер отвечает ОК. - 3. Отправляется код из письма + новый email. Сервер отправляет код2 на новый email. - 4. Отправлются оба кода и новый email. Если они проходят проверку, то сервер меняет email пользователя на новый и возвращает ОК. -*/ -app.post('/auth/email/change-email', (req, res, next) => { - const email2 = String(req.body.email ?? '').trim() - const code = String(req.body.code ?? '').trim() - const code2 = String(req.body.code2 ?? '').trim() - - const email = db - .prepare('select email from customers where id = :customer_id') - .pluck(true) - .get(res.locals) - - const stepNo = !code ? 1 : code && !email ? 2 : code && email && !code2 ? 3 : code && email && code2 ? 4 : -1 - if (stepNo == -1) - throw Error('BAD_STEP::400') - - if (stepNo == 1) { - const code = Math.random().toString().substr(2, 4) - cache['change-email'][email] = code - sendEmail(email, 'CHANGE-EMAIL', `${email} => ${code}`) - } - - if (stepNo == 2) { - if (cache['change-email'][email] != code) - throw Error('INCORRECT_CODE::400') - } - - if (stepNo == 3) { - if (!checkEmail(email2)) - throw Error('INCORRECT_EMAIL::400') - - const code2 = Math.random().toString().substr(2, 4) - cache['change-email2'][email2] = code2 - sendEmail(email2, 'CHANGE-EMAIL2', `${email2} => ${code2}`) - } - - if (stepNo == 4) { - if (cache['change-email'][email] != code || cache['change-email2'][email2] != code2) - throw Error('INCORRECT_CODE::400') - - const info = db - .prepare('update customers set email = :email where id = :customer_id') - .run(res.locals) - - if (info.changes == 0) - throw Error('BAD_REQUEST::400') - - delete cache['change-email'][email] - delete cache['change-email2'][email2] - } - - res.status(200).json({success: true}) -}) - -/* - Смена пароля/восстановление доступа выполняется за ТРИ последовательных вызова - 1. Отравляется пустой закпрос для смены запоса и email, в случае восстановления доступа. Сервер на email отправляет код. - 2. Отправляется email + код из письма. Если указан корректный код, то сервер отвечает ОК. - 3. Отправляется email + код из письма + новый пароль. Сервер изменяет пароль и возвращает ОК. -*/ -app.post('/auth/email/:action(change-password|recovery)', (req, res, next) => { - const code = String(req.body.code ?? '').trim() - const password = String(req.body.password) - const action = req.params.action - - const email = action == 'change-password' ? db - .prepare('select email from customers where id = :customer_id') - .pluck(true) - .get(res.locals) : - String(req.body.email ?? '').trim() - - const stepNo = action == 'change-password' ? - (!code && !password ? 1 : code && !password ? 2 : code && password ? 3 : -1) : - (!email && !code && !password ? 1 : email && code && !password ? 2 : email && code && password ? 3 : -1) - if (stepNo == -1) - throw Error('BAD_STEP::400') - - if (stepNo == 1) { - if (!checkEmail(email)) - throw Error('INCORRECT_EMAIL::400') - - const code = Math.random().toString().substr(2, 4) - cache[action][email] = code - sendEmail(email, action.toUpperCase(), `${email} => ${code}`) - } - - if (stepNo == 2) { - if (cache[action][email] != code) - throw Error('INCORRECT_CODE::400') - } - - if (stepNo == 3) { - if (cache[action][email] != code) - throw Error('INCORRECT_CODE::400') - - if (!checkPassword(password)) - throw Error('INCORRECT_PASSWORD::400') - - const info = db - .prepare('update customers set password = :password where email = :email') - .run({ email, password }) - - if (info.changes == 0) - throw Error('BAD_REQUEST::400') - - delete cache[action][email] - } - - res.status(200).json({success: true}) -}) - -app.get('/auth/logout', (req, res, next) => { - if (req.session?.asid) - delete sessions[req.session.asid] - - res.setHeader('Set-Cookie', [`asid=; expired; httpOnly;path=/api/admin`]) - res.status(200).json({success: true}) -}) - -// CUSTOMER -app.get('/customer/profile', (req, res, next) => { - const row = db - .prepare(` - select id, name, email, plan, - coalesce(json_balance, '{}') json_balance, coalesce(json_company, '{}') json_company, - upload_chat_id, generate_key(-id, :time) upload_token - from customers - where id = :customer_id - `) - .get(res.locals) - - if (row?.upload_chat_id) { - row.upload_chat = db - .prepare(`select id, name, telegram_id from chats where id = :chat_id and project_id is null`) - .safeIntegers(true) - .get({ chat_id: row.upload_chat_id}) - delete row.upload_chat_id - } - - for (const key in row) { - if (key.startsWith('json_')) { - row[key.substr(5)] = JSON.parse(row[key]) - delete row[key] - } - } - - res.status(200).json({success: true, data: row}) -}) - -app.put('/customer/profile', (req, res, next) => { - if (req.body.company instanceof Object) - req.body.json_company = JSON.stringify(req.body.company) - else - delete req.body?.json_company - - const info = db - .prepareUpdate( - 'customers', - ['name', 'password', 'json_company'], - req.body, - ['id']) - .run(Object.assign({}, req.body, {id: req.session.customer_id})) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true}) -}) - -app.get('/customer/settings', (req, res, next) => { - const row = db - .prepare(`select coalesce(json_settings, '{}') from customers where id = :customer_id`) - .pluck(true) - .get(res.locals) - - res.status(200).json({success: true, data: JSON.parse(row)}) -}) - -app.put('/customer/settings', (req, res, next) => { - res.locals.json_settings = JSON.stringify(req.body || {}) - - db - .prepare(`update customers set json_settings = :json_settings where id = :customer_id`) - .run(res.locals) - - res.status(200).json({success: true}) -}) - -// PROJECT -function getProject(id, customer_id) { - const row = db - .prepare(` - select id, name, description, logo, is_logo_bg, is_archived, - (select count(*) from chats where project_id = p.id) chat_count, - (select count(distinct user_id) from chat_users where chat_id in (select id from chats where project_id = p.id)) user_count - from projects p - where customer_id = :customer_id and p.id = :id - order by name - `) - .get({id, customer_id}) - - if (!row) - throw Error('NOT_FOUND::404') - - row.is_archived = Boolean(row.is_archived) - row.is_logo_bg = Boolean(row.is_logo_bg) - - return row -} - -app.get('/project', (req, res, next) => { - const data = db - .prepare(` - select id, name, description, logo, is_logo_bg, is_archived, - (select count(*) from chats where project_id = p.id) chat_count, - (select count(distinct user_id) from chat_users where chat_id in (select id from chats where project_id = p.id)) user_count - from projects p - where customer_id = :customer_id - order by name - `) - .all(res.locals) - - data.forEach(row => { - row.is_archived = Boolean(row.is_archived) - row.is_logo_bg = Boolean(row.is_logo_bg) - }) - - res.status(200).json({success: true, data}) -}) - -app.post('/project', (req, res, next) => { - res.locals.name = req.body?.name - res.locals.description = req.body?.description - res.locals.logo = req.body?.logo - res.locals.is_logo_bg = 'is_logo_bg' in req.body ? +req.body.is_logo_bg : undefined - - const id = db - .prepare(` - insert into projects (customer_id, name, description, logo, is_logo_bg) - values (:customer_id, :name, :description, :logo, :is_logo_bg) - returning id - `) - .pluck(true) - .get(res.locals) - - const data = getProject(id, res.locals.customer_id) - res.status(200).json({success: true, data}) -}) - -app.use ('(/project/:pid(\\d+)/*|/project/:pid(\\d+))', (req, res, next) => { - res.locals.project_id = parseInt(req.params.pid) - - const row = db - .prepare('select 1 from projects where id = :project_id and customer_id = :customer_id and is_archived <> 1') - .get(res.locals) - - if (!row) - throw Error('ACCESS_DENIED::401') - - next() -}) - -app.get('/project/:pid(\\d+)', (req, res, next) => { - const data = getProject(req.params.pid, res.locals.customer_id) - res.status(200).json({success: true, data}) -}) - -app.put('/project/:pid(\\d+)', (req, res, next) => { - res.locals.id = req.params.pid - res.locals.name = req.body?.name - res.locals.description = req.body?.description - res.locals.logo = req.body?.logo - res.locals.is_logo_bg = 'is_logo_bg' in req.body ? +req.body.is_logo_bg : undefined - - const info = db - .prepareUpdate( - 'projects', - ['name', 'description', 'logo', 'is_logo_bg'], - res.locals, - ['id', 'customer_id']) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - const data = getProject(req.params.pid, res.locals.customer_id) - res.status(200).json({success: true, data}) -}) - -app.put('/project/:pid(\\d+)/:action(archive|restore)', async (req, res, next) => { - res.locals.id = req.params.pid - res.locals.is_archived = +(req.params.action == 'archive') - - const info = db - .prepare(` - update projects - set is_archived = :is_archived - where id = :id and customer_id = :customer_id and coalesce(is_archived, 0) = not :is_archived - `) - .run(res.locals) - - if (info.changes == 0) - throw Error('BAD_REQUEST::400') - - const chatIds = db - .prepare(`select id from chats where project_id = :id`) - .pluck(true) - .all(res.locals) - - for (const chatId of chatIds) { - await bot.sendMessage(chatId, res.locals.is_archived ? 'Проект помещен в архив. Отслеживание сообщений прекращено.' : 'Проект восстановлен из архива.') - } - - const data = getProject(req.params.pid, res.locals.customer_id) - res.status(200).json({success: true, data}) -}) - -// USER -function getUser(id, project_id) { - const row = db - .prepare(` - select u.id, u.telegram_id, u.firstname, u.lastname, u.username, u.photo, - ud.fullname, ud.email, ud.phone, ud.role, ud.department, ud.is_blocked, - cu.company_id - from users u - left join user_details ud on u.id = ud.user_id and ud.project_id = :project_id - left join company_users cu on u.id = cu.user_id - where id = :id - `) - .safeIntegers(true) - .all({id, project_id}) - - if (!row) - throw Error('NOT_FOUND::404') - - row.is_blocked = Boolean(row.is_blocked) - - return row -} - -app.get('/project/:pid(\\d+)/user', (req, res, next) => { - const data = db - .prepare(` - select u.id, u.telegram_id, u.firstname, u.lastname, u.username, u.photo, - ud.fullname, ud.email, ud.phone, ud.role, ud.department, ud.is_blocked, - cu.company_id - from users u - left join user_details ud on u.id = ud.user_id and ud.project_id = :project_id - left join company_users cu on u.id = cu.user_id - where id in ( - select user_id - from chat_users - where chat_id in (select id from chats where project_id = :project_id) - ) - `) - .safeIntegers(true) - .all(res.locals) - - data.forEach(row => { - row.is_blocked = Boolean(row.is_blocked) - }) - - res.status(200).json({success: true, data}) -}) - -app.get('/project/:pid(\\d+)/user/:uid(\\d+)', (req, res, next) => { - const data = getUser(req.params.uid, req.params.pid) - res.status(200).json({success: true, data}) -}) - -app.put('/project/:pid(\\d+)/user/:uid(\\d+)', (req, res, next) => { - res.locals.user_id = parseInt(req.params.uid) - - res.locals.fullname = req.body?.fullname - res.locals.email = req.body?.email - res.locals.phone = req.body?.phone - res.locals.role = req.body?.role - res.locals.department = req.body?.department - res.locals.is_blocked = 'is_blocked' in req.body ? +req.body.is_blocked : undefined - - const info = db - .prepareUpsert('user_details', - ['fullname', 'email', 'phone', 'role', 'department', 'is_blocked'], - res.locals, - ['user_id', 'project_id'] - ) - .run(res.locals) - - const data = getUser(req.params.uid, req.params.pid) - res.status(200).json({success: true, data}) -}) - -app.get('/project/:pid(\\d+)/token', (req, res, next) => { - res.locals.time = Math.floor(Date.now() / 1000) - - const key = db - .prepare('select generate_key(id, :time) from projects where id = :project_id and customer_id = :customer_id') - .pluck(true) - .get(res.locals) - - if (!key) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: key}) -}) - -// COMPANY -function getCompany(id, project_id) { - const row = db - .prepare(` - select id, name, address, email, phone, site, description, logo, - (select json_group_array(user_id) from company_users where company_id = c.id) users - from companies c - where c.id = :id and project_id = :project_id - order by name - `) - .get({id, project_id}) - - if (!row) - throw Error('NOT_FOUND::404') - - return row -} - -app.get('/project/:pid(\\d+)/company', (req, res, next) => { - const data = db - .prepare(` - select id, name, address, email, phone, site, description, logo, - (select json_group_array(user_id) from company_users where company_id = c.id) users - from companies c - where project_id = :project_id - order by name - `) - .all(res.locals) - - data.forEach(row => row.users = JSON.parse(row.users || '[]')) - - res.status(200).json({success: true, data}) -}) - -app.get('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { - const data = getCompany(req.params.cid, req.params.pid) - res.status(200).json({success: true, data}) -}) - -app.post('/project/:pid(\\d+)/company', (req, res, next) => { - res.locals.name = req.body?.name - res.locals.address = req.body?.address - res.locals.email = req.body?.email - res.locals.phone = req.body?.phone - res.locals.site = req.body?.site - res.locals.description = req.body?.description - res.locals.logo = req.body?.logo - - const id = db - .prepare(` - insert into companies (project_id, name, address, email, phone, site, description, logo) - values (:project_id, :name, :address, :email, :phone, :site, :description, :logo) - returning id - `) - .pluck(true) - .get(res.locals) - - const data = getCompany(id, req.params.pid) - res.status(200).json({success: true, data}) -}) - -app.put('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { - res.locals.id = parseInt(req.params.cid) - res.locals.name = req.body?.name - res.locals.address = req.body?.address - res.locals.email = req.body?.email - res.locals.phone = req.body?.phone - res.locals.site = req.body?.site - res.locals.description = req.body?.description - res.locals.logo = req.body?.logo - - const info = db - .prepareUpdate( - 'companies', - ['name', 'address', 'email', 'phone', 'site', 'description', 'logo'], - res.locals, - ['id', 'project_id']) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - const data = getCompany(req.params.cid, req.params.pid) - res.status(200).json({success: true, data}) -}) - -app.delete('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { - res.locals.company_id = req.params.cid - - const info = db - .prepare(`delete from companies where id = :company_id and project_id = :project_id`) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: {id: req.params.cid}}) -}) - -app.put('/project/:pid(\\d+)/company/:cid(\\d+)/user', (req, res, next) => { - res.locals.company_id = parseInt(req.params.cid) - - // Проверка, что есть доступ к компании - const row = db - .prepare('select 1 from companies where id = :company_id and project_id = :project_id') - .get(res.locals) - - if (!row) - throw Error('NOT_FOUND::404') - - const user_ids = req.body instanceof Array ? [...new Set(req.body.map(e => parseInt(e)))] : [] - - // Проверка, что пользователи имеют доступ к проекту - let rows = db - .prepare(` - select user_id - from chat_users - where chat_id in (select id from chats where project_id = :project_id) - `) - .pluck(true) // .raw? - .get(res.locals) - - if (user_ids.some(user_id => !rows.contains(user_id))) - throw Error('INACCESSABLE_MEMBER::400') - - // Проверка, что пользователи не участвуют в других компаниях на проекте - rows = db - .prepare(` - select user_id - from company_users - where company_id in (select id from companies where id <> :company_id and project_id = :project_id) - `) - .pluck(true) // .raw? - .get(res.locals) - - if (user_ids.some(user_id => !rows.contains(user_id))) - throw Error('USED_MEMBER::400') - - db - .prepare(`delete from company_users where company_id = :company_id`) - .run(res.locals) - - db - .prepare(` - insert into company_users (company_id, user_id) - select :company_id, value from json_each(:json_ids) - `) - .run(res.locals, {json_ids: JSON.stringify(user_ids)}) - - res.status(200).json({success: true}) -}) - -// CHATS -function getChat(id, project_id) { - const row = db - .prepare(` - select id, name, telegram_id, is_channel, description, logo, user_count, bot_can_ban - from chats c - where c.id = :id and project_id = :project_id - `) - .all({id, project_id}) - - if (!row) - throw Error('NOT_FOUND::404') - - row.is_channel = Boolean(row.is_channel) - row.bot_can_ban = Boolean(row.bot_can_ban) - - return row -} - -app.get('/project/:pid(\\d+)/chat', (req, res, next) => { - const data = db - .prepare(` - select id, name, telegram_id, is_channel, description, logo, user_count, bot_can_ban - from chats - where project_id = :project_id - `) - .all(res.locals) - - data.forEach(row => { - row.is_channel = Boolean(row.is_channel) - row.bot_can_ban = Boolean(row.bot_can_ban) - }) - - res.status(200).json({success: true, data}) -}) - -app.get('/project/:pid(\\d+)/chat/:gid(\\d+)', (req, res, next) => { - const data = getChat(req.params.gid, req.params.pid) - res.status(200).json({success: true, data}) -}) - -app.delete('/project/:pid(\\d+)/chat/:gid(\\d+)', async (req, res, next) => { - res.locals.chat_id = req.params.gid - const info = db - .prepare(`update chats set project_id = null where id = :chat_id and project_id = :project_id`) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - await bot.sendMessage(res.locals.chat_id, 'Чат удален из проекта') - - res.status(200).json({success: true, data: {id: req.params.gid}}) -}) - -module.exports = app \ No newline at end of file diff --git a/backend/apps/bot.js b/backend/apps/bot.js deleted file mode 100644 index 0f84d29..0000000 --- a/backend/apps/bot.js +++ /dev/null @@ -1,627 +0,0 @@ -const db = require('../include/db') - -const { Api, TelegramClient } = require('telegram') -const { StringSession } = require('telegram/sessions') -const { Button } = require('telegram/tl/custom/button') -const { CustomFile } = require('telegram/client/uploads') - -let session -let client -let BOT_ID -const BOT_NAME = 'ready_or_not_2025_bot' - -function registerUser (telegramId) { - db - .prepare(`insert or ignore into users (telegram_id) values (:telegram_id)`) - .safeIntegers(true) - .run({ telegram_id: telegramId }) - - return db - .prepare(`select id from users where telegram_id = :telegram_id`) - .safeIntegers(true) - .pluck(true) - .get({ telegram_id: telegramId }) -} - -function updateUser (userId, data) { - const info = db - .prepare(` - update users set firstname = :firstname, lastname = :lastname, username = :username, - access_hash = :access_hash, language_code = :language_code where id = :user_id - `) - .safeIntegers(true) - .run({ - user_id: userId, - firstname: data.firstName, - lastname: data.lastName, - username: data.username, - access_hash: data.accessHash.value, - language_code: data.langCode - }) - - return info.changes == 1 -} - -async function updateUserPhoto (userId, data) { - const photoId = data.photo?.photoId?.value - if (!photoId) - return - - const tgUserId = db - .prepare(`select telegram_id from users where id = :user_id and coalesce(photo_id, 0) <> :photo_id`) - .safeIntegers(true) - .pluck(true) - .get({user_id: userId, photo_id: photoId }) - - if (!tgUserId) - return - - const photo = await client.invoke(new Api.photos.GetUserPhotos({ - userId: new Api.PeerUser({ userId: tgUserId }), - maxId: photoId, - offset: -1, - limit: 1, - })); - - const file = await client.downloadFile(new Api.InputPhotoFileLocation({ - id: photoId, - accessHash: photo.photos[0]?.accessHash, - fileReference: Buffer.from('random'), - thumbSize: 'a', - }, {})) - - db - .prepare(`update users set photo_id = :photo_id, photo = :photo where id = :user_id`) - .safeIntegers(true) - .run({ user_id: userId, photo_id: photoId, photo: 'data:image/jpg;base64,' + file.toString('base64') }) -} - -async function registerChat (telegramId, isChannel) { - const chat = db - .prepare(`select id, name, is_channel, access_hash from chats where telegram_id = :telegram_id`) - .safeIntegers(true) - .get({telegram_id: telegramId}) - - if (chat && chat.access_hash && chat.is_channel == isChannel && chat.name) - return chat.id - - const entity = isChannel ? { channelId: telegramId } : { chatId: telegramId } - const tgChat = await client.getEntity( isChannel ? - new Api.InputPeerChannel(entity) : - new Api.InputPeerChat(entity) - ) - - const chatId = db - .prepare(`replace into chats (telegram_id, is_channel, access_hash, name) values (:telegram_id, :is_channel, :access_hash, :name) returning id`) - .safeIntegers(true) - .pluck(true) - .get({ - telegram_id: telegramId, - is_channel: +isChannel, - access_hash: tgChat.accessHash.value, - name: tgChat.title - }) - - await updateChat(chatId) - - return chatId -} - -async function updateChat (chatId) { - const chat = db - .prepare(`select id, telegram_id, access_hash, is_channel from chats where id = :id`) - .safeIntegers(true) - .get({id: chatId}) - - const peer = chat.is_channel ? - new Api.InputPeerChannel({ channelId: chat.telegram_id, accessHash: chat.access_hash }) : - new Api.InputPeerChat({ chatId: chat.telegram_id, accessHash: chat.access_hash }) - - const data = chat.is_channel ? - await client.invoke(new Api.channels.GetFullChannel({ channel: peer })) : - await client.invoke(new Api.messages.GetFullChat({ chatId: chat.telegram_id, accessHash: chat.access_hash })) - - const file = data?.fullChat?.chatPhoto ? await client.downloadFile(new Api.InputPeerPhotoFileLocation({ peer, photoId: data.fullChat.chatPhoto?.id }, {})) : null - logo = file ? 'data:image/jpg;base64,' + file.toString('base64') : null - - db - .prepare(`update chats set description = :description, logo = :logo, user_count = :user_count, last_update_time = :last_update_time where id = :id`) - .safeIntegers(true) - .run({ - id: chatId, - description: data.fullChat.about, - logo, - user_count: data.fullChat.participantsCount - (data.users || []).filter(user => user.bot).length, - last_update_time: Math.floor(Date.now() / 1000) - }) -} - -async function attachChat(chatId, projectId) { - const chat = db - .prepare(`update chats set project_id = :project_id where id = :chat_id returning telegram_id, access_hash, is_channel`) - .safeIntegers(true) - .get({ chat_id: chatId, project_id: projectId }) - - if (!chat.telegram_id) - return console.error('Can\'t attach chat: ' + chatId + ' to project: ' + projectId) - - const peer = chat.is_channel ? - new Api.InputPeerChannel({ channelId: chat.telegram_id, accessHash: chat.access_hash }) : - new Api.InputPeerChat({ chatId: chat.telegram_id, accessHash: chat.access_hash }) - - const message = db - .prepare(`select (select name from customers where id = p.customer_id) || ' >> ' || p.name from projects p where id = :project_id`) - .pluck(true) - .get({project_id: projectId}) - - const resultBtn = await client.sendMessage(peer, { - message, - buttons: client.buildReplyMarkup([[Button.url('Открыть проект', `https://t.me/${BOT_NAME}/userapp?startapp=` + projectId)]]) - }) - - await client.invoke(new Api.messages.UpdatePinnedMessage({ - peer, - id: resultBtn.id, - unpin: false - })) -} - -async function reloadChatUsers(chatId, onlyReset) { - db - .prepare(`delete from chat_users where chat_id = :chat_id`) - .run({ chat_id: chatId }) - - if (onlyReset) - return - - const chat = db - .prepare(`select telegram_id, is_channel, access_hash from chats where id = :chat_id`) - .get({ chat_id: chatId}) - - if (!chat) - return - - const tgChatId = chat.telegram_id - const isChannel = chat.is_channel - const accessHash = chat.access_hash - - const result = isChannel ? - await client.invoke(new Api.channels.GetParticipants({ - channel: new Api.PeerChannel({ channelId: tgChatId, accessHash }), - filter: new Api.ChannelParticipantsRecent(), - limit: 999999, - offset: 0 - })) : await client.invoke(new Api.messages.GetFullChat({ chatId: tgChatId, accessHash })) - - const users = result.users.filter(user => !user.bot) - for (const user of users) { - const userId = registerUser(user.id.value, user) - - if (updateUser(userId, user)) { - await updateUserPhoto (userId, user) - - db - .prepare(`insert or ignore into chat_users (chat_id, user_id) values (:chat_id, :user_id)`) - .run({ chat_id: chatId, user_id: userId }) - } - } - - db - .prepare(`update chats set user_count = (select count(1) from chat_users where chat_id = :chat_id) where id = :chat_id`) - .run({ chat_id: chatId}) -} - -async function registerUpload(data) { - if (!data.projectId || !data.media) - return console.error ('registerUpload: ' + (data.projectId ? 'media' : 'project id') + ' is missing') - - const customer_id = db - .prepare(`select customer_id from projects where project_id = :project_id`) - .pluck(true) - .get({project_id: data.projectId}) - - if (!customer_id) - return console.error ('registerUpload: The customer is not found for project: ' + data.projectId) - - const chat = db - .prepare( - `select id, telegram_id, project_id, is_channel, access_hash from chats - where id = (select upload_chat_id from customers where id = :customer_id`) - .safeIntegers(true) - .get({ customer_id }) - - if (!chat || !chat.telegram_id || chat.id == data.originchatId) - return console.error ('registerUpload: The upload chat is not set for customer: ' + customer_id) - - const peer = chat.is_channel ? - new Api.PeerChannel({ channelId: chat.telegram_id, accessHash: chat.access_hash }) : - new Api.PeerChat({ chatlId: chat.telegram_id, accessHash: chat.access_hash }) - - let resultId = 0 - - try { - const result = await client.invoke(new Api.messages.SendMedia({ - peer, - media: data.media, - message: data.caption || '', - background: true, - silent: true - })) - - const update = result.updates.find(u => - (u.className == 'UpdateNewMessage' || u.className == 'UpdateNewChannelMessage') && - u.message.className == 'Message' && - (u.message.peerId.channelId?.value == chat.telegram_id || u.message.peerId.chatId?.value == chat.telegram_id) && - u.message.media) - - const udoc = update?.message?.media?.document - if (udoc) { - resultId = db - .prepare(` - insert into files (project_id, origin_chat_id, origin_message_id, chat_id, message_id, - file_id, access_hash, filename, mime, caption, size, published_by, published, parent_type, parent_id) - values (:project_id, :origin_chat_id, :origin_message_id, :chat_id, :message_id, - :file_id, :access_hash, :filename, :mime, :caption, :size, :published_by, :published, :parent_type, :parent_id) - returning id - `) - .safeIntegers(true) - .pluck(true) - .get({ - project_id: data.projectId, - origin_chat_id: data.originchatId, - origin_message_id: data.originMessageId, - chat_id: chat.id, - message_id: update.message.id, - file_id: udoc.id.value, - filename: udoc.attributes.find(attr => attr.className == 'DocumentAttributeFilename')?.fileName, - access_hash: udoc.accessHash.value, - mime: udoc.mimeType, - caption: data.caption, - size: udoc.size.value, - published_by: data.publishedBy, - published: data.published, - parent_type: data.parentType, - parent_id: data.parentId - }) - } - - } catch (err) { - //fs.appendFileSync('./1.log', '\n\nERR:' + err.message + ':' + JSON.stringify (err.stack)+'\n\n') - console.error('registerUpload: ' + err.message) - } - - return resultId -} - -async function onNewServiceMessage (msg, isChannel) { - const action = msg.action || {} - const tgChatId = isChannel ? msg.peerId?.channelId?.value : msg.peerId?.chatId?.value - const chatId = await registerChat(tgChatId, isChannel) - - // Сhat rename - if (action.className == 'MessageActionChatEditTitle') { - const info = db - .prepare(` - update chats - set name = :name, is_channel = :is_channel, last_update_time = :last_update_time - where telegram_id = :telegram_id - `) - .safeIntegers(true) - .run({ - name: action.title, - is_channel: +isChannel, - last_update_time: Math.floor (Date.now() / 1000), - telegram_id: tgChatId - }) - - if (info.changes == 0) - console.error('onNewServiceMessage: Can\'t update a chat title: ' + tgChatId) - } - - // Chat to Channel - if (action.className == 'MessageActionChatMigrateTo') { - const info = db - .prepare(` - update chats - set telegram_id = :new_telegram_id, name = :name, is_channel = 1, last_update_time = :last_update_time - where telegram_id = :old_telegram_id - `) - .safeIntegers(true) - .run({ - name: action.title, - last_update_time: Date.now() / 1000, - old_telegram_id: tgChatId, - new_telegram_id: action.channelId.value - }) - - if (info.changes == 0) - console.error('onNewServiceMessage: Can\'t apply a chat migration to channel: ' + tgChatId) - } - - // User/s un/register - if (action.className == 'MessageActionChatAddUser' || action.className == 'MessageActionChatDeleteUser' || - action.className == 'MessageActionChannelAddUser' || action.className == 'MessageActionChannelDeleteUser' - ) { - - const tgUserIds = [action.user, action.users, action.userId].flat().filter(Boolean).map(e => BigInt(e.value)) - const isAdd = action.className == 'MessageActionChatAddUser' || action.className == 'MessageActionChannelAddUser' - - if (tgUserIds.indexOf(BOT_ID) == -1) { - // Add/remove non-bot users - for (const tgUserId of tgUserIds) { - const userId = registerUser(tgUserId) - - if (isAdd) { - try { - const user = await client.getEntity(new Api.PeerUser({ userId: tgUserId })) - updateUser(userId, user) - await updateUserPhoto (userId, user) - } catch (err) { - console.error(msg.className + ', ' + userId + ': ' + err.message) - } - } - - const query = isAdd ? - `insert or ignore into chat_users (chat_id, user_id) values (:chat_id, :user_id)` : - `delete from chat_users where chat_id = :chat_id and user_id = :user_id` - db - .prepare(query) - .run({ chat_id: chatId, user_id: userId }) - } - } - } -} - -async function onNewMessage (msg, isChannel) { - const tgChatId = isChannel ? msg.peerId?.channelId?.value : msg.peerId?.chatId?.value - const chatId = await registerChat(tgChatId, isChannel) - - // Document is detected - if (msg.media?.document) { - const doc = msg.media.document - - const projectId = db - .prepare(`select project_id from chats where telegram_id = :telegram_id`) - .safeIntegers(true) - .pluck(true) - .get({telegram_id: tgChatId}) - - const media = new Api.InputMediaDocument({ - id: new Api.InputDocument({ - id: doc.id.value, - accessHash: doc.accessHash.value, - fileReference: doc.fileReference - }) - }) - - await registerUpload({ - projectId, - media, - caption: msg.message, - originchatId: chatId, - originMessageId: msg.id, - parentType: 0, - publishedBy: registerUser (msg.fromId?.userId?.value), - published: msg.date - }) - } - - if (msg.message?.startsWith(`/start@${BOT_NAME} KEY-`) || msg.message?.startsWith('KEY-')) { - const rows = db - .prepare(` - select 1 from chats where id = :chat_id and project_id is not null - union all - select 1 from customers where upload_chat_id = :chat_id - `) - .all({ chat_id: chatId }) - - if (rows.length) - return await sendMessage(chatId, 'Чат уже используется') - - const rawkey = msg.message.substr(msg.message?.indexOf('KEY-')) - const [_, time64, key] = rawkey.split('-') - const now = Math.floor(Date.now() / 1000) - const time = Buffer.from(time64, 'base64') - - if (now - 3600 >= time && time >= now) - return await sendMessage(chatId, 'Время действия ключа для привязки истекло') - - const row = db - .prepare(` - select (select id from projects where generate_key(id, :time) = :rawkey) project_id, - (select id from customers where generate_key(-id, :time) = :rawkey) customer_id - `) - .get({ rawkey, time }) - - if (row.project_id) { - await attachChat(chatId, row.project_id) - await reloadChatUsers(chatId) - } - - if (row.customer_id) { - const info = db - .prepare(`update customers set upload_chat_id = :chat_id where id = :customer_id`) - .safeIntegers(true) - .run({ customer_id: row.customer_id, chat_id: chatId }) - - if (info.changes == 0) - console.error('Can\'t set upload chat: ' + chatId + ' to customer: ' + row.customer_id) - } - } -} - -async function onNewUserMessage (msg) { - if (msg.message == '/start' && msg.peerId?.className == 'PeerUser') { - const tgUserId = msg.peerId?.userId?.value - const userId = registerUser(tgUserId) - - try { - const user = await client.getEntity(new Api.PeerUser({ userId: tgUserId })) - updateUser(userId, user) - await updateUserPhoto (userId, user) - - const appButton = new Api.KeyboardButtonWebView({ - text: "Open Mini-App", // Текст на кнопке - url: "https://h5sj0gpz-3000.euw.devtunnels.ms/", // URL вашего Mini-App (HTTPS!) - }); - - - const inputPeer = new Api.InputPeerUser({userId: tgUserId, accessHash: user.accessHash.value}) - await client.sendMessage(inputPeer, { - message: 'Сообщение от бота', - buttons: client.buildReplyMarkup([ - [Button.url('Админка', `https://t.me/${BOT_NAME}/userapp?startapp=admin`)], - [Button.url('Пользователь', `https://t.me/${BOT_NAME}/userapp?startapp=user`)], - [appButton] - ]) - }) - } catch (err) { - console.error(msg.className + ', ' + userId + ': ' + err.message) - } - } -} - -async function onUpdatePaticipant (update, isChannel) { - const tgChatId = isChannel ? update.channelId?.value : update.chatlId?.value - if (!tgChatId || update.userId?.value != BOT_ID) - return - - const chatId = await registerChat (tgChatId, isChannel) - - const isBan = update.prevParticipant && !update.newParticipant - const isAdd = (!update.prevParticipant || update.prevParticipant?.className == 'ChannelParticipantBanned') && update.newParticipant - - if (isBan || isAdd) - await reloadChatUsers(chatId, isBan) - - if (isBan) { - db - .prepare(`update chats set project_id = null where id = :chat_id`) - .run({chat_id: chatId}) - } - - const botCanBan = update.newParticipant?.adminRights?.banUsers || 0 - db - .prepare(`update chats set bot_can_ban = :bot_can_ban where id = :chat_id`) - .run({chat_id: chatId, bot_can_ban: +botCanBan}) -} - -async function uploadFile(projectId, fileName, mime, data, parentType, parentId, publishedBy) { - const file = await client.uploadFile({ file: new CustomFile(fileName, data.length, '', data), workers: 1 }) - - const media = new Api.InputMediaUploadedDocument({ - file, - mimeType: mime, - attributes: [new Api.DocumentAttributeFilename({ fileName })] - }) - - return await registerUpload({ - projectId, - media, - parentType, - parentId, - publishedBy, - published: Math.floor(Date.now() / 1000) - }) -} - -async function downloadFile(projectId, fileId) { - const file = db - .prepare(` - select file_id, access_hash, '' thumbSize, filename, mime - from files where id = :id and project_id = :project_id - `) - .safeIntegers(true) - .get({project_id: projectId, id: fileId}) - - if (!file) - return false - - const result = await client.downloadFile(new Api.InputDocumentFileLocation({ - id: file.file_id, - accessHash: file.access_hash, - fileReference: Buffer.from(file.filename), - thumbSize: '' - }, {})) - - return { - filename: file.filename, - mime: file.mime, - size: result.length, - data: result - } -} - -async function sendMessage (chatId, message) { - const chat = db - .prepare(`select telegram_id, is_channel from chats where id = :chat_id`) - .get({ chat_id: chatId}) - - if (!chat) - return - - const entity = chat.is_channel ? { channelId: chat.telegram_id } : { chatId: chat.telegram_id } - const inputPeer = await client.getEntity( chat.is_channel ? - new Api.InputPeerChannel(entity) : - new Api.InputPeerChat(entity) - ) - - await client.sendMessage(inputPeer, {message}) - - const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) - await delay(1000) -} - -async function leaveChat (chatId) { - const chat = db - .prepare(`select telegram_id, access_hash, is_channel from chats where id = :chat_id`) - .get({ chat_id: chatId}) - - if (!chat) - return - - if (chat.is_channel) { - const inputPeer = await client.getEntity(new Api.InputPeerChannel({ channelId: chat.telegram_id, accessHash: chat.access_hash })) - await client.invoke(new Api.channels.LeaveChannel({ channel: inputPeer })) - } else { - await client.invoke(new Api.messages.DeleteChatUser({ chatId: chat.telegram_id, userId: this.id, accessHash: chat.access_hash })) - } -} - -async function start (apiId, apiHash, botAuthToken, sid) { - BOT_ID = BigInt(botAuthToken.split(':')[0]) - - session= new StringSession(sid || '') - client = new TelegramClient(session, apiId, apiHash, {}) - - client.addEventHandler(async (update) => { - if (update.className == 'UpdateConnectionState') - return - - try { - // console.log(update) - - if (update.className == 'UpdateNewMessage' || update.className == 'UpdateNewChannelMessage') { - const msg = update?.message - const isChannel = update.className == 'UpdateNewChannelMessage' - - if (!msg) - return - - const result = msg.peerId?.className == 'PeerUser' ? await onNewUserMessage(msg) : - msg.className == 'MessageService' ? await onNewServiceMessage(msg, isChannel) : - await onNewMessage(msg, isChannel) - } - - if (update.className == 'UpdateChatParticipant' || update.className == 'UpdateChannelParticipant') - await onUpdatePaticipant(update, update.className == 'UpdateChannelParticipant') - } catch (err) { - console.error(err) - } - }) - - await client.start({botAuthToken}) -} - -module.exports = { start, uploadFile, downloadFile, reloadChatUsers, sendMessage } - diff --git a/backend/apps/miniapp.js b/backend/apps/miniapp.js deleted file mode 100644 index 7a72498..0000000 --- a/backend/apps/miniapp.js +++ /dev/null @@ -1,643 +0,0 @@ -const express = require('express') -const multer = require('multer') -const crypto = require('crypto') -const fs = require('fs') -const contentDisposition = require('content-disposition') - -const bot = require('./bot') -const db = require('../include/db') - -const app = express.Router() -const upload = multer({ - storage: multer.memoryStorage(), - limits: { - fileSize: 10_000_000 // 10mb - } -}) - -function hasAccess(project_id, user_id) { - return !!db - .prepare(` - select 1 - from group_users - where user_id = :user_id and - group_id in (select id from groups where project_id = :project_id) and - not exists(select 1 from user_details where user_id = :user_id and project_id = :project_id and is_blocked = 1) and - not exists(select 1 from projects where id = :project_id and is_deleted = 1) - `) - .get({project_id, user_id}) -} - -const sessions = {} - -app.use((req, res, next) => { - if (req.path == '/user/login') - return next() - - const sid = req.query.sid || req.cookies.sid - req.session = sessions[sid] - if (!req.session) - throw Error('ACCESS_DENIED::401') - - res.locals.user_id = req.session.user_id - next() -}) - - -app.post('/user/login', (req, res, next) => { - db - .prepare(`insert or ignore into users (telegram_id) values (:telegram_id)`) - .safeIntegers(true) - .run(res.locals) - - const user_id = db - .prepare(`select id from users where telegram_id = :telegram_id`) - .safeIntegers(true) - .pluck(true) - .get(res.locals) - - const sid = crypto.randomBytes(64).toString('hex') - req.session = sessions[sid] = {sid, user_id} - res.setHeader('Set-Cookie', [`sid=${sid};httpOnly;path=/`]) - res.locals.user_id = user_id - - res.status(200).json({success: true}) -}) - -app.get('/project', (req, res, next) => { - const where = req.query.id ? ' and p.id = ' + parseInt(req.query.id) : '' - - const rows = db - .prepare(` - select p.id, p.name, p.description, p.logo, - c.name customer_name, c.upload_group_id <> 0 has_upload - from projects p - inner join customers c on p.customer_id = c.id - where p.id in ( - select project_id - from groups - where id in (select group_id from group_users where user_id = :user_id) - ) and not exists(select 1 from user_details where user_id = :user_id and project_id = p.id and is_blocked = 1) - ${where} and is_deleted <> 1 - `) - .all(res.locals) - - if (where && rows.length == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: where ? rows[0] : rows}) -}) - -app.get('/project/:pid(\\d+)', (req, res, next) => { - res.redirect(req.baseUrl + `/project?id=${req.params.pid}`) -}) - -app.use('/project/:pid(\\d+)/*', (req, res, next) => { - res.locals.project_id = parseInt(req.params.pid) - - if (!hasAccess(res.locals.project_id, res.locals.user_id)) - throw Error('ACCESS_DENIED::401') - - const row = db - .prepare('select customer_id from projects where id = :project_id') - .get(res.locals) - - res.locals.customer_id = row.customer_id - - next() -}) - -app.get('/project/:pid(\\d+)/user', (req, res, next) => { - const where = req.query.id ? ' and u.id = ' + parseInt(req.query.id) : '' - - const users = db - .prepare(` - with actuals (user_id) as ( - select distinct user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) - and group_id in (select group_id from group_users where user_id = :user_id) - ), - contributors (user_id) as ( - select created_by from tasks where project_id = :project_id - union - select assigned_to from tasks where project_id = :project_id - union - select created_by from meetings where project_id = :project_id - union - select published_by from documents where project_id = :project_id - ), - members (user_id, is_leave) as ( - select user_id, 0 is_leave from actuals - union all - select user_id, 1 is_leave from contributors where user_id not in (select user_id from actuals) - ) - select u.id, - u.telegram_id, - u.username, - u.firstname, - u.lastname, - u.photo, - u.json_phone_projects, - ud.fullname, - ud.role, - ud.department, - ud.is_blocked, - (select company_id - from company_users - where user_id = u.id and - company_id in (select id from companies where project_id = :project_id)) company_id, - m.is_leave - from users u - inner join members m on u.id = m.user_id - left join user_details ud on ud.user_id = u.id and ud.project_id = :project_id - where 1 = 1 ${where} - `) - .all(res.locals) - - const companies = db - .prepare('select id, name, email, phone, site, description from companies where project_id = :project_id') - .all(res.locals) - .reduce((companies, row) => { - companies[row.id] = row - return companies - }, {}) - - const mappings = {} - const company_id = users.find(m => m.id == res.locals.user_id).company_id - if (company_id) { - res.locals.company_id = company_id - - db - .prepare('select show_as_id, show_to_id from company_mappings where project_id = :project_id and company_id = :company_id') - .all(res.locals) - .forEach(row => mappings[row.show_to_id] = row.show_to_id) - } - - users.forEach(m => { - m.company = companies[mappings[m.company_id] || m.company_id] - delete m.company_id - }) - - users.forEach(m => { - const isHide = JSON.parse(m.json_phone_projects || []).indexOf(res.locals.project_id) == -1 - if (isHide) - delete m.phone - delete m.json_phone_projects - }) - - if (where && users.length == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: where ? users[0] : users}) -}) - -app.get('/project/:pid(\\d+)/user/reload', async (req, res, next) => { - const groupIds = db - .prepare(`select id from groups where project_id = :project_id`) - .all(res.locals) - .map(e => e.id) - - const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) - - for (const groupId of groupIds) { - await bot.reloadGroupUsers(groupId) - await sleep(1000) - } - - res.status(200).json({success: true}) -}) - -app.get('/project/:pid(\\d+)/group', (req, res, next) => { - const where = req.query.id ? ' and id = ' + parseInt(req.query.id) : '' - - const rows = db - .prepare(` - select id, name, telegram_id - from groups - where project_id = :project_id and id in (select group_id from group_users where user_id = :user_id) - ${where} - `) - .all(res.locals) - - if (where && rows.length == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: where ? rows[0] : rows}) -}) - -app.get('/project/:pid(\\d+)/group/:gid(\\d+)', (req, res, next) => { - res.redirect(req.baseUrl + `/project/${req.params.pid}/group?id=${req.params.gid}`) -}) - -// TASK -app.get('/project/:pid(\\d+)/task', (req, res, next) => { - const where = req.query.id ? ' and t.id = ' + parseInt(req.query.id) : '' - - const rows = db - .prepare(` - select id, name, created_by, assigned_to, priority, status, time_spent, create_date, plan_date, close_date, - (select json_group_array(user_id) from task_users where task_id = t.id) observers, - (select json_group_array(id) from documents where parent_type = 1 and parent_id = t.id) attachments - from tasks t - where project_id = :project_id and - (created_by = :user_id or assigned_to = :user_id or exists(select 1 from task_users where task_id = t.id and user_id = :user_id)) - ${where} - `) - .all(res.locals) - - rows.forEach(row => { - row.observers = JSON.parse(row.observers) - row.attachments = JSON.parse(row.attachments) - }) - - if (where && rows.length == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: where ? rows[0] : rows}) -}) - -app.get('/project/:pid(\\d+)/task/:tid(\\d+)', (req, res, next) => { - res.redirect(req.baseUrl + `/project/${req.params.pid}/task?id=${req.params.tid}`) -}) - -app.post('/project/:pid(\\d+)/task', (req, res, next) => { - res.locals.name = req.body?.name - res.locals.status = parseInt(req.body?.status) - res.locals.priority = parseInt(req.body?.priority) - res.locals.assigned_to = req.body?.assigned_to ? parseInt(req.body?.assigned_to) : undefined - res.locals.create_date = Math.floor(Date.now() / 1000) - res.locals.plan_date = req.body?.plan_date ? parseInt(req.body?.plan_date) : undefined - - if (res.locals.assigned_to && !hasAccess(res.locals.project_id, res.locals.assigned_to)) - throw Error('INCORRECT_ASSIGNED_TO::400') - - const id = db - .prepare(` - insert into tasks (project_id, name, created_by, assigned_to, priority, status, create_date, plan_date) - values (:project_id, :name, :user_id, :assigned_to, :priority, :status, :create_date, :plan_date) - returning id - `) - .pluck(true) - .get(res.locals) - - res.status(200).json({success: true, data: id}) -}) - -app.use('/project/:pid(\\d+)/task/:tid(\\d+)*', (req, res, next) => { - res.locals.task_id = req.params.tid - - const task = db - .prepare(` - select created_by, assigned_to - from tasks - where id = :task_id and project_id = :project_id - and (created_by = :user_id or assigned_to = :user_id or exists(select 1 from task_users where task_id = :task_id and user_id = :user_id)) - `) - .get(res.locals) - - if (!task) - throw Error('NOT_FOUND::404') - - res.locals.is_author = task.created_by == res.locals.user_id - res.locals.is_assigned = task.assigned_to == res.locals.user_id - - next() -}) - -app.put('/project/:pid(\\d+)/task/:tid(\\d+)', (req, res, next) => { - if (!res.locals.is_author && !res.locals.is_assigned) - throw Error('ACCESS_DENIED::401') - - res.locals.id = res.locals.task_id - res.locals.name = req.body?.name - res.locals.status = parseInt(req.body?.status) - res.locals.priority = parseInt(req.body?.priority) - res.locals.assigned_to = req.body?.assigned_to ? parseInt(req.body?.assigned_to) : undefined - res.locals.plan_date = req.body?.plan_date ? parseInt(req.body?.plan_date) : undefined - - const columns = res.locals.is_author ? ['name', 'assigned_to', 'priority', 'status', 'plan_date', 'time_spent'] : ['status', 'time_spent'] - const info = db - .prepareUpdate('tasks', columns, res.locals, ['id', 'project_id']) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true}) -}) - -app.delete('/project/:pid(\\d+)/task/:tid(\\d+)', (req, res, next) => { - if (!res.locals.is_author) - throw Error('ACCESS_DENIED::401') - - const info = db - .prepare(`delete from tasks where id = :task_id and project_id = :project_id and created_by = :user_id`) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true}) -}) - -app.put('/project/:pid(\\d+)/task/:tid(\\d+)/observer', (req, res, next) => { - if (!res.locals.is_author && !res.locals.is_assigned) - throw Error('ACCESS_DENIED::401') - - const user_ids = req.body instanceof Array ? [...new Set(req.body.map(e => parseInt(e)))] : [] - - // Проверка, что выбранные пользователи имеют доступ к проекту - let rows = db - .prepare(` - select user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) - `) - .pluck(true) - .all(res.locals) - - if (user_ids.some(user_id => !rows.contains(user_id))) - throw Error('INACCESSABLE_USER::400') - - res.locals.json_ids = JSON.stringify(user_ids) - - db - .prepare(` - delete from task_users where task_id = :task_id; - insert into task_users (task_id, user_id) select :task_id, value from json_each(:json_ids) - `) - .run(res.locals) - - res.status(200).json({success: true}) -}) - -// MEETINGS -app.get('/project/:pid(\\d+)/meeting', (req, res, next) => { - const where = req.query.id ? ' and m.id = ' + parseInt(req.query.id) : '' - - const rows = db - .prepare(` - select id, name, description, created_by, meet_date, - (select json_group_array(user_id) from meeting_users where meeting_id = m.id) participants, - (select json_group_array(id) from documents where parent_type = 2 and parent_id = m.id) attachments - from meetings m - where project_id = :project_id and - (created_by = :user_id or exists(select 1 from meeting_users where meeting_id = m.id and user_id = :user_id)) - ${where} - `) - .all(res.locals) - - rows.forEach(row => { - row.participants = JSON.parse(row.participants) - row.attachments = JSON.parse(row.attachments) - }) - - if (where && rows.length == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: where ? rows[0] : rows}) -}) - -app.get('/project/:pid(\\d+)/meeting/:mid(\\d+)', (req, res, next) => { - res.redirect(req.baseUrl + `/project/${req.params.pid}/meeting?id=${req.params.mid}`) -}) - -app.post('/project/:pid(\\d+)/meeting', (req, res, next) => { - res.locals.name = req.body?.name - res.locals.description = req.body?.description - res.locals.meet_date = req.body?.meet_date ? parseInt(req.body?.meet_date) : undefined - - const id = db - .prepare(` - insert into meetings (project_id, name, description, created_by, meet_date) - values (:project_id, :name, :description, :user_id, :meet_date) - returning id - `) - .pluck(true) - .get(res.locals) - - res.status(200).json({success: true, data: id}) -}) - -app.use('/project/:pid(\\d+)/meeting/:mid(\\d+)*', (req, res, next) => { - res.locals.meeting_id = req.params.mid - - const meeting = db - .prepare(` - select created_by - from meetings - where id = :meeting_id and project_id = :project_id - and (created_by = :user_id or exists(select 1 from meeting_users where meeting_id = :meeting_id and user_id = :user_id)) - `) - .get(res.locals) - - if (!meeting) - throw Error('NOT_FOUND::404') - - res.locals.is_author = meeting.created_by == res.locals.user_id - - next() -}) - -app.put('/project/:pid(\\d+)/meeting/:mid(\\d+)', (req, res, next) => { - if (!res.locals.is_author) - throw Error('ACCESS_DENIED::401') - - res.locals.id = res.locals.meeting_id - res.locals.name = req.body?.name - res.locals.description = req.body?.description - res.locals.meet_date = req.body?.meet_date ? parseInt(req.body?.meet_date) : undefined - - const info = db - .prepareUpdate('meetings', ['name', 'description', 'meet_date'], res.locals, ['id', 'project_id']) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true}) -}) - -app.delete('/project/:pid(\\d+)/meeting/:mid(\\d+)', (req, res, next) => { - if (!res.locals.is_author) - throw Error('ACCESS_DENIED::401') - - const info = db - .prepare(`delete from meetings where id = :meeting_id and project_id = :project_id and created_by = :user_id`) - .run(res.locals) - - if (info.changes == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true}) -}) - -app.put('/project/:pid(\\d+)/meeting/:mid(\\d+)/participants', (req, res, next) => { - if (!res.locals.is_author) - throw Error('ACCESS_DENIED::401') - - const user_ids = req.body instanceof Array ? [...new Set(req.body.map(e => parseInt(e)))] : [] - - // Проверка, что выбранные пользователи имеют доступ к проекту - let rows = db - .prepare(` - select user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) - `) - .pluck(true) // .raw? - .all(res.locals) - - if (user_ids.some(user_id => rows.indexOf(user_id)) == -1) - throw Error('INACCESSABLE_USER::400') - - db - .prepare(`delete from meeting_users where meeting_id = :meeting_id`) - .run(res.locals) - - res.locals.json_ids = JSON.stringify(user_ids) - db - .prepare(`insert into meeting_users (meeting_id, user_id) select :meeting_id, value from json_each(:json_ids)`) - .run(res.locals) - - res.status(200).json({success: true}) -}) - -// DOCUMENTS -app.get('/project/:pid(\\d+)/document', (req, res, next) => { - const ids = String(req.query.id).split(',').map(e => parseInt(e)).filter(e => e > 0) - const where = ids.length > 0 ? ' and id in (' + ids.join(', ') + ')' : '' - - // Документы - // 1. Из групп, которые есть в проекте и в которых участвует пользователь - // 2. Из задач проекта, где пользователь автор, ответсвенный или наблюдатель - // 3. Из встреч на проекте, где пользователь создатель или участник - // To-Do: отдавать готовую ссылку --> как минимум GROUP_ID надо заменить на tgGroupId - const rows = db - .prepare(` - select id, origin_group_id, origin_message_id, filename, mime, caption, size, published_by, parent_id, parent_type - from documents d - where project_id = :project_id ${where} and ( - origin_group_id in (select group_id from group_users where user_id = :user_id) - or - parent_type = 1 and parent_id in ( - select id - from tasks t - where project_id = :project_id and (created_by = :user_id or assigned_to = :user_id or exists(select 1 from task_users where task_id = t.id and user_id = :user_id)) - ) - or - parent_type = 2 and parent_id in ( - select id - from meetings m - where project_id = :project_id and (created_by = :user_id or exists(select 1 from meeting_users where meeting_id = m.id and user_id = :user_id)) - ) - ) - `) - .all(res.locals) - - if (where && rows.length == 0) - throw Error('NOT_FOUND::404') - - res.status(200).json({success: true, data: ids.length == 1 ? rows[0] : rows}) -}) - -app.post('/project/:pid(\\d+)/:type(task|meeting)/:id(\\d+)/attach', upload.any(), async (req, res, next) => { - const parentType = req.params.type == 'task' ? 1 : 2 - const parentId = req.params.id - - const ids = [] - for (const file of req.files) { - const id = await bot.uploadDocument(res.locals.project_id, file.originalname, file.mimetype, file.buffer, parentType, parentId, res.locals.user_id) - ids.push(id) - } - - res.redirect(req.baseUrl + `/project/${req.params.pid}/document?id=` + ids.join(',')) -}) - -app.use('/project/:pid(\\d+)/document/:did(\\d+)', (req, res, next) => { - res.locals.document_id = req.params.did - - const doc = db - .prepare(`select * from documents where id = :document_id and project_id = :project_id`) - .get(res.locals) - - if (!doc) - throw Error('NOT_FOUND::404') - - if (doc.parent_type == 0) { - res.locals.group_id = doc.group_id - const row = db - .prepare(`select 1 from group_users where group_id = :group_id and user_id = :user_id`) - .get(res.locals) - - if (row) { - res.locals.can_download = true - } - } else { - res.locals.parent_id = doc.parent_id - const parent = doc.parent_type == 1 ? 'task' : 'meeting' - - const row = db - .prepare(` - select 1 - from ${parent}s - where id = :parent_id and project_id = :project_id or - exists(select 1 from ${parent}_users where ${parent}_id = :parent_id and user_id = :user_id) - `) - .get(res.locals) - - if (row) { - res.locals.can_download = true - res.locals.can_delete = doc.published_by == res.locals.user_id - } - } - - next() -}) - -app.get('/project/:pid(\\d+)/document/:did(\\d+)', async (req, res, next) => { - if (!res.locals.can_download) - throw Error('NOT_FOUND::404') - - const file = await bot.downloadDocument(res.locals.project_id, res.locals.document_id) - res.writeHead(200, { - 'Content-Length': file.size, - 'Content-Type': file.mime, - 'Content-Disposition': contentDisposition(file.filename) - }) - - res.end(file.data) -}) - -app.delete('/project/:pid(\\d+)/document/:id(\\d+)', (req, res, next) => { - if (!res.locals.can_delete) - throw Error('NOT_FOUND::404') - - const doc = db - .prepare(`delete from documents where id = :id and project_id = :project_id`) - .run(res.locals) - - res.status(200).json({success: true}) -}) - -app.get('/settings', (req, res, next) => { - const row = db - .prepare(`select coalesce(json_settings, '{}') from users where id = :user_id`) - .pluck(true) - .get(res.locals) - - res.status(200).json({success: true, data: JSON.parse(row)}) -}) - -app.put('/settings', (req, res, next) => { - res.locals.json_settings = JSON.stringify(req.body || {}) - - const row = db - .prepare(`update users set json_settings = :json_settings where id = :user_id`) - .run(res.locals) - - res.status(200).json({success: true}) -}) - -module.exports = app \ No newline at end of file diff --git a/backend/data/db.sqlite b/backend/data/db.sqlite deleted file mode 100644 index 438da8f3ada2c774a2fa3df0cc38a9223d1b6020..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94208 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 diff --git a/backend/data/db.sqlite-shm b/backend/data/db.sqlite-shm deleted file mode 100644 index 3d2446dc3793ec894aaf968434e185afa8691acb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI*Jq`gu6bJBGpYc)XIDw9Xa1f=|5%v}oYNd0C6NpNpR4RBQB3kYG{gXFucQe`K z{SGiQI!{tgGpa~w(2hQgDi7<;@jl$%7OT^)Q_lAN%Y3}NPDelPk6*pgiuLi8_vcTm z$J2LpGtT}e+~mLa zo_liccaurx&*Av4V-_RN8+Hg;BC6a=%e(#=r*!eb#0+L@ci3{giNuTT_;&jke^tYw z`@KBA0kQcTNq#SXnBT?U!T+4!!QaE*NVNe41V8`;KmY_l00ck)1V8`;KmY_lU{VEm zY|@!X$&%+N6)K(9!i-MZ(sHy4jTQ}>vq(omsZi+BRcTt1$G{ckSxU7`m9Ee-I84%( z(8;tJTACS2P9xGZSvhKL2#=s@TEPy{nMxv`nh~XYv9T z!o?!inZ#De3ryc`o5HjZTIGC4ThtqITPh8=FiQP1lj-Vu@6O2!cSN z_=Ze3XfvciL5UGjZ%L$K!FUHkG(4{pzQfUkBXDW;w@CHiH-`wb!u@u zBkid;`Kfr7POZarPEz&dY7G@%!SMoRW)AgSRH1rou297+#gB1#aX?xWg+kCm98YSQ z@XXqSklzUY*1BzCTXGdbt?Lo8>zml-UW*XF1fiC0wM-*>+!^YnK}$_V=x!rIJUr)? zRS*8p+xYR0P8>UNEuM3(F8CM+e4bhOmWDEs0=Xb5QXx4K;m8|#Au;kXT}DBOMiqj( zBk>=}q?5R?7z{d-&El}xEH;~C#^Z9#%+1(rE}v`8v#{V>aJZHNOA7&xEvQBad?0lc zozAl0usL|^e_2dlBLN%LqFRy=BAS381%&B1a>UnWjbQMrGB*GQOpP!%!NLTt4u~lHCaG|LWnUlBy0VzQK z=)s;#6?Qdd*soC^KdHaEZikR}k+39IX zbfso(!o%{Azlyt^MRix0eUcP~w{@%`nQ+Q=b1K>6_4=ucoBN|nma{qnhl?6?p&!hB zu;l3VE7c}+IPceMZl3$E&Ahlf;6{XgLrIf^k!vaaoGes!JDJeTvYX~5D}HO9Q&?D# zG}Xgx$xy;T@YOf%FP|g_UMt|rl8vDkH;_#})2*6vk9Pj6HP>hfs1t<>r@}p^SodxfqJ*rBvPIm7#o7L++Wi|E>#84Y z2*{}S$nud#yT_PNi(hly+WN}!auMHc`reB2)gP%ZTnhcC;}1788~W$?C-ytNUAyVv z+xhmlMmoEzMiuTKghvL?Xsp;ZByj&GL)#&~f7$x?MDk9!cl$b?b+#8>zo6@A?_Hla z4jq_&Y#`{j?b8lO)3@~)Bb4<_-@9+LEsOk4a((w2l~LV#gT5IuTl1Dl56T$)d-~y%sI`rZNJ&;-v9Cbp^}^1 zMOEAW+$?pAu<><{AAWCTyQ3ufcAULu=kM90;T{*ZmX;kXka7F=bV^^zBnHLz_2<>M zQXNAARc-Ns4I;Z+a@FoU=|J+nq&UI%8WZvxJ?t44>NAl4e#sywN&5Fzui!^J6BsLt zIThz4+Z?{FJoC`O>o2P|zt;5O&*ta4_R6;gaP1-j?<5u)hl}pSo*c1~EGbfmLz`DG zvo~&Dwn-^@^LRyvr%|@DZAYow zD=n)J6)BfW&N_BJ^e%23JyzhASu@+P-MGnikUx8^w7t);K1W}6t-rS7VE4X#r7g>& zsxIL7lZ^*~KMI-CsJ9mNfj6&ZjiH(!oFpDP=NhO=(J1mYIn+lZ-p$3!lX(XT*O5iH z4_KsA$pRA3J;*oUc?Tch6(}G80w4eaAOHd&00JNY0w4eaAOHd{rvQhf)2JLd8e)@l zf=YUmERxQlas~K=s1(4Hyg+SDPiejFuiuT07toK57tp_)rwzRZ0T2KI5C8!X_&*Zx zcsxFUX=R1anfg(L`tCC '' and length(name) < 256), - email text check(email is null or trim(email) <> '' and length(email) < 128), - password text check(password is null or length(password) > 7 and length(password) < 64), - telegram_id integer, - plan integer, - json_balance text default '{}', - is_blocked integer default 0, - json_company text default '{}', - upload_chat_id integer, - json_backup_server text default '{}', - json_backup_params text default '{}', - json_settings text default '{}' -) strict; - -create table if not exists projects ( - id integer primary key autoincrement, - customer_id integer references customers(id) on delete cascade, - name text not null check(trim(name) <> '' and length(name) < 256), - description text check(description is null or length(description) < 4096), - logo text, - is_logo_bg integer default 0, - is_archived integer default 0 -) strict; - -create table if not exists chats ( - id integer primary key autoincrement, - project_id integer references projects(id) on delete cascade, - name text, - telegram_id integer, - description text, - logo text, - access_hash integer, - is_channel integer check(is_channel in (0, 1)) default 0, - bot_can_ban integer default 0, - owner_id integer references users(id) on delete set null, - user_count integer, - last_update_time integer -); -create unique index if not exists idx_chats_telegram_id on chats (telegram_id); - -create table if not exists users ( - id integer primary key autoincrement, - telegram_id integer, - access_hash integer, - firstname text check(firstname is null or length(firstname) < 256), - lastname text check(lastname is null or length(lastname) < 256), - username text check(username is null or length(username) < 256), - photo_id integer, - photo text, - language_code text, - json_settings text default '{}' -) strict; -create unique index if not exists idx_users_telegram_id on users (telegram_id); - -create table if not exists user_details ( - user_id integer references users(id) on delete cascade, - project_id integer references projects(id) on delete cascade, - fullname text check(fullname is null or length(fullname) < 256), - email text check(email is null or length(email) < 256), - phone text check(phone is null or length(phone) < 256), - role text check(role is null or length(role) < 256), - department text check(department is null or length(department) < 256), - is_blocked integer check(is_blocked in (0, 1)) default 0, - primary key (user_id, project_id) -) strict; - -create table if not exists tasks ( - id integer primary key autoincrement, - project_id integer references projects(id) on delete cascade, - name text not null check(trim(name) <> '' and length(name) < 4096), - created_by integer references users(id) on delete set null, - assigned_to integer references users(id) on delete set null, - closed_by integer references users(id) on delete set null, - priority integer check(priority in (0, 1, 2, 3, 4, 5)) default 0, - status integer check(status >= 1 and status <= 10) default 1, - time_spent integer check(time_spent is null or time_spent > 0 and time_spent < 44640), -- one month - create_date integer, - plan_date integer, - close_date integer -) strict; - -create table if not exists meetings ( - id integer primary key autoincrement, - project_id integer references projects(id) on delete cascade, - name text not null check(trim(name) <> '' and length(name) < 4096), - description text check(description is null or length(description) < 4096), - created_by integer references users(id) on delete set null, - meet_date integer -) strict; - -create table if not exists files ( - id integer primary key autoincrement, - project_id integer references projects(id) on delete cascade, - origin_chat_id integer references chats(id) on delete set null, - origin_message_id integer, - chat_id integer references chats(id) on delete set null, - message_id integer, - file_id integer, - access_hash integer, - filename text not null check(length(filename) < 256), - mime text check(mime is null or length(mime) < 128), - caption text check(caption is null or length(caption) < 4096), - size integer, - published_by integer references users(id) on delete set null, - published integer, - parent_type integer check(parent_type in (0, 1, 2)) default 0, - parent_id integer, - backup_state integer default 0 -) strict; - -create table if not exists companies ( - id integer primary key autoincrement, - project_id integer references projects(id) on delete cascade, - name text not null check(length(name) < 4096), - address text check(address is null or length(address) < 512), - email text check(email is null or length(email) < 128), - phone text check(phone is null or length(phone) < 128), - site text check(site is null or length(site) < 128), - description text check(description is null or length(description) < 4096), - logo text -) strict; - -create table if not exists company_mappings ( - project_id integer references projects(id) on delete cascade, - company_id integer references companies(id) on delete cascade, - show_as_id integer references companies(id) on delete cascade, - show_to_id integer references companies(id) on delete cascade -) strict; - -create table if not exists task_users ( - task_id integer references tasks(id) on delete cascade, - user_id integer references users(id) on delete cascade, - primary key (task_id, user_id) -) without rowid; - -create table if not exists meeting_users ( - meeting_id integer references meetings(id) on delete cascade, - user_id integer references users(id) on delete cascade, - primary key (meeting_id, user_id) -) without rowid; - -create table if not exists company_users ( - company_id integer references companies(id) on delete cascade, - user_id integer references users(id) on delete cascade, - primary key (company_id, user_id) -) without rowid; - -create table if not exists chat_users ( - chat_id integer references chats(id) on delete cascade, - user_id integer references users(id) on delete cascade, - primary key (chat_id, user_id) -) without rowid; - -pragma foreign_keys = on; - - -create trigger if not exists trg_chats_update after update on chats -when NEW.project_id is null -begin - delete from chat_users where chat_id = NEW.id; -end; - - - - - - diff --git a/backend/include/db.js b/backend/include/db.js deleted file mode 100644 index 7d65f17..0000000 --- a/backend/include/db.js +++ /dev/null @@ -1,57 +0,0 @@ -const fs = require('fs') -const crypto = require('crypto') -const sqlite3 = require('better-sqlite3') - -const db = sqlite3(`./data/db.sqlite`) -db.pragma('journal_mode = WAL') - -db.exec(fs.readFileSync('./data/init.sql', 'utf8')) - -/* -db.exec(`attach database './data/backup.sqlite' as backup`) - -db.function('backup', (tblname, ...values) => { - db - .prepare(`insert into backup.${tblname} select ` + values.map(e => '?').join(', ')) - .run(values) -}) - -const backupQuery = db - .prepare(` - select group_concat(tbl || char(13) || trg, char(13) || char(13)) from ( - select 'create table if not exists backup.' || t.name || ' (' || group_concat(c.name || ' ' || c.type, ', ') || ', time integer);' tbl, - 'create trigger if not exists trg_' || t.name || '_delete after delete on ' || t.name || ' for each row begin ' || - ' select backup (' || t.name || ',' || group_concat('OLD.' || c.name, ', ') || ', strftime(''%s'', ''now'')); end;' trg - from sqlite_master t left join pragma_table_xinfo c on t.tbl_name = c.arg and c.schema = 'main' - where t.sql is not null and t.type = 'table' and t.name <> 'sqlite_sequence' - group by t.type, t.name order by t.type, t.name) - `) - .pluck(true) - .get() -db.exec(backupQuery) -*/ - -db.function('generate_key', (id, time) => { - return [ - 'KEY', - Buffer.from(time + '').toString('base64'), - crypto.createHash('md5').update(`sa${time}-${id}lt`).digest('hex') - ].join('-') -}) - -process.on('exit', () => db.close()) -process.on('SIGHUP', () => process.exit(128 + 1)) -process.on('SIGINT', () => process.exit(128 + 2)) -process.on('SIGTERM', () => process.exit(128 + 15)) - -db.prepareUpdate = function (table, columns, data, where) { - const dataColumns = columns.filter(col => col in data) - if (dataColumns.length == 0) - throw Error('SQLite Error: No data to update') - - return db.prepare(`update "${table}" ` + - `set ` + dataColumns.map(col => `"${col}" = :${col}`).join(', ') + - ` where ` + where.map(col => `"${col}" = :${col}`).join(' and ')) -} - -module.exports = db \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index d1bef8c..0000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,1990 +0,0 @@ -{ - "name": "telegram-bot", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "telegram-bot", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "better-sqlite3": "^11.8.0", - "body-parser": "^1.20.3", - "content-disposition": "^0.5.4", - "cookie-parser": "^1.4.7", - "express": "^4.21.2", - "express-session": "^1.18.1", - "multer": "^1.4.5-lts.1", - "nodemailer": "^6.9.16", - "telegram": "^2.26.16" - } - }, - "node_modules/@cryptography/aes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@cryptography/aes/-/aes-0.1.1.tgz", - "integrity": "sha512-PcYz4FDGblO6tM2kSC+VzhhK62vml6k6/YAkiWtyPvrgJVfnDRoHGDtKn5UiaRRUrvUTTocBpvc2rRgTCqxjsg==", - "license": "GPL-3.0-or-later" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/async-mutex": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", - "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.1" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/better-sqlite3": { - "version": "11.9.1", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.9.1.tgz", - "integrity": "sha512-Ba0KR+Fzxh2jDRhdg6TSH0SJGzb8C0aBY4hR8w8madIdIzzC6Y1+kx5qR6eS1Z+Gy20h6ZU28aeyg0z1VIrShQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - } - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bufferutil": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", - "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", - "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", - "license": "MIT", - "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-session": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", - "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", - "license": "MIT", - "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.7", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express-session/node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "license": "ISC" - }, - "node_modules/node-abi": { - "version": "3.74.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", - "integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-localstorage": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz", - "integrity": "sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==", - "license": "MIT", - "dependencies": { - "write-file-atomic": "^1.1.4" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/nodemailer": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", - "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "license": "(MIT AND Zlib)" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/real-cancellable-promise": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/real-cancellable-promise/-/real-cancellable-promise-1.2.1.tgz", - "integrity": "sha512-JwhiWJTMMyzFYfpKsiSb8CyQktCi1MZ8ZBn3wXvq28qXDh8Y5dM7RYzgW3r6SV22JTEcof8pRsvDp4GxLmGIxg==", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", - "license": "ISC", - "engines": { - "node": "*" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", - "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/store2": { - "version": "2.14.4", - "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz", - "integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==", - "license": "MIT" - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tar-fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", - "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/telegram": { - "version": "2.26.22", - "resolved": "https://registry.npmjs.org/telegram/-/telegram-2.26.22.tgz", - "integrity": "sha512-EIj7Yrjiu0Yosa3FZ/7EyPg9s6UiTi/zDQrFmR/2Mg7pIUU+XjAit1n1u9OU9h2oRnRM5M+67/fxzQluZpaJJg==", - "license": "MIT", - "dependencies": { - "@cryptography/aes": "^0.1.1", - "async-mutex": "^0.3.0", - "big-integer": "^1.6.48", - "buffer": "^6.0.3", - "htmlparser2": "^6.1.0", - "mime": "^3.0.0", - "node-localstorage": "^2.2.1", - "pako": "^2.0.3", - "path-browserify": "^1.0.1", - "real-cancellable-promise": "^1.1.1", - "socks": "^2.6.2", - "store2": "^2.13.0", - "ts-custom-error": "^3.2.0", - "websocket": "^1.0.34" - }, - "optionalDependencies": { - "bufferutil": "^4.0.3", - "utf-8-validate": "^5.0.5" - } - }, - "node_modules/telegram/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/telegram/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/ts-custom-error": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", - "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "license": "ISC" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "license": "MIT", - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/websocket": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", - "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", - "license": "Apache-2.0", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.63", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", - "integrity": "sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==", - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "slide": "^1.1.5" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", - "engines": { - "node": ">=0.10.32" - } - } - } -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index 8c38a14..0000000 --- a/backend/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "telegram-bot", - "version": "1.0.0", - "main": "app.js", - "directories": { - "doc": "docs" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "description": "", - "dependencies": { - "better-sqlite3": "^11.8.0", - "body-parser": "^1.20.3", - "content-disposition": "^0.5.4", - "cookie-parser": "^1.4.7", - "express": "^4.21.2", - "express-session": "^1.18.1", - "multer": "^1.4.5-lts.1", - "nodemailer": "^6.9.16", - "telegram": "^2.26.16" - } -} diff --git a/backend/public/index.html b/backend/public/index.html deleted file mode 100644 index 254ff81..0000000 --- a/backend/public/index.html +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/public/miniapp.html b/backend/public/miniapp.html deleted file mode 100644 index 7c786fb..0000000 --- a/backend/public/miniapp.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - -
- - -
- -
-
ПРОЕКТЫ
-
EMPTY
-
- -
-
УЧАСТНИКИ
-
-
- - - - - - \ No newline at end of file diff --git a/i18n-2.xlsm b/i18n-2.xlsm index 912d8ec3581d048e2d005254cd3eb33bc5528d71..3e365fab00afb1db867baaa5f9aec6d3b5265ded 100644 GIT binary patch delta 35223 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 diff --git a/package-lock.json b/package-lock.json index 32966f1..25a90a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@quasar/cli": "^2.5.0", "@quasar/extras": "^1.16.4", "axios": "^1.2.1", - "dayjs": "^1.11.13", "pinia": "^2.0.11", "quasar": "^2.16.0", "vue": "^3.4.18", @@ -4594,12 +4593,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" - }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", diff --git a/package.json b/package.json index 687f01a..2961338 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "@quasar/cli": "^2.5.0", "@quasar/extras": "^1.16.4", "axios": "^1.2.1", - "dayjs": "^1.11.13", "pinia": "^2.0.11", "quasar": "^2.16.0", "vue": "^3.4.18", diff --git a/quasar.config.ts b/quasar.config.ts index 8312784..833953a 100644 --- a/quasar.config.ts +++ b/quasar.config.ts @@ -60,40 +60,11 @@ export default defineConfig((ctx) => { // extendTsConfig (tsConfig) {} }, - vueRouterMode: 'history', // available values: 'hash', 'history' - // vueDevtools: true, // Должно быть true - // devtool: 'source-map', // Для лучшей отладки - // vueRouterBase, - // vueDevtools, - // vueOptionsAPI: false, - - // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup - - // publicPath: '/', - // analyze: true, - // env: {}, - // rawDefine: {} - // ignorePublicFolder: true, - // minify: false, - // polyfillModulePreload: true, - // distDir - - // extendViteConf (viteConf) {}, - // viteVuePluginOptions: {}, - - // from deepseek + vueRouterMode: 'history', vite: { plugins: [ ['@intlify/unplugin-vue-i18n/vite', { - // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false` - // compositionOnly: false, - - // if you want to use named tokens in your Vue I18n messages, such as 'Hello {name}', - // you need to set `runtimeOnly: false` - // runtimeOnly: false, - ssr: ctx.modeName === 'ssr', - // you need to set i18n resource including paths ! include: [ fileURLToPath(new URL('./src/i18n', import.meta.url)) ] }], @@ -105,8 +76,12 @@ export default defineConfig((ctx) => { useFlatConfig: true } }, { server: false }] - ] - }, + ], + + // Конфигурация сборки Rollup + build: { + } + } }, // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#devserver diff --git a/src/App.vue b/src/App.vue index 5acbc42..52017c1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -7,7 +7,6 @@ import { useRouter } from 'vue-router' import { useAuthStore } from 'stores/auth' import { useSettingsStore } from 'stores/settings' - import { useProjectsStore } from 'stores/projects' import { useQuasar } from 'quasar' import type { WebApp } from '@twa-dev/types' @@ -51,7 +50,6 @@ const authStore = useAuthStore() const settingsStore = useSettingsStore() - const projectsStore = useProjectsStore() const startRouteInfo = ref<{ id: number; taskId?: number; meetingId?: number } | null>(null) if (tg.initDataUnsafe.start_param) { @@ -59,9 +57,8 @@ } onMounted(async () => { - console.log('app mount') try { - if (startRouteInfo.value) projectsStore.setStartRouteInfo(startRouteInfo.value) + if (startRouteInfo.value) authStore.setStartRouteInfo(startRouteInfo.value) if (!authStore.isInit) await authStore.init(tg) if (!settingsStore.isInit) await settingsStore.init() } catch { diff --git a/src/boot/axios.ts b/src/boot/axios.ts index aa0b788..d691321 100644 --- a/src/boot/axios.ts +++ b/src/boot/axios.ts @@ -1,34 +1,40 @@ import { defineBoot } from '#q-app/wrappers' -import axios, { type AxiosInstance } from 'axios' +import axios, { type AxiosError } from 'axios' -declare module 'vue' { - interface ComponentCustomProperties { - $axios: AxiosInstance; - $api: AxiosInstance; +class ServerError extends Error { + constructor( + public code: string, + message: string + ) { + super(message) + this.name = 'ServerError' } } -// Be careful when using SSR for cross-request state pollution -// due to creating a Singleton instance here; -// If any client changes this (global) instance, it might be a -// good idea to move this instance creation inside of the -// "export default () => {}" function below (which runs individually -// for each client) const api = axios.create({ baseURL: '/api/miniapp', - withCredentials: true // Важно для работы с cookies + withCredentials: true }) +api.interceptors.response.use( + response => response, + async (error: AxiosError<{ error?: { code: string; message: string } }>) => { + const errorData = error.response?.data?.error || { + code: 'ZERO', + message: error.message || 'Unknown error' + } + + const serverError = new ServerError( + errorData.code, + errorData.message + ) + + return Promise.reject(serverError) + } +) + export default defineBoot(({ app }) => { - // for use inside Vue files (Options API) through this.$axios and this.$api - - app.config.globalProperties.$axios = axios - // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form) - // so you won't necessarily have to import axios in each vue file - app.config.globalProperties.$api = api - // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form) - // so you can easily perform requests against your app's API }) -export { api } +export { api, ServerError } diff --git a/src/components/companyInfoPersons.vue b/src/components/companyInfoPersons.vue index ce1be05..15a2af1 100644 --- a/src/components/companyInfoPersons.vue +++ b/src/components/companyInfoPersons.vue @@ -49,7 +49,6 @@ ] async function goPersonInfo () { - console.log('update') await router.push({ name: 'person_info' }) } diff --git a/src/components/meetingBlock.vue b/src/components/meetingBlock.vue index 7eb5b8a..f8475d2 100644 --- a/src/components/meetingBlock.vue +++ b/src/components/meetingBlock.vue @@ -296,7 +296,6 @@ return Object.values(validations).every(Boolean) }) - const initialMeeting = ref({} as MeetingParams) onMounted(() => { diff --git a/src/components/pnImageSelector.vue b/src/components/pnImageSelector.vue index 006096c..45e1bfb 100644 --- a/src/components/pnImageSelector.vue +++ b/src/components/pnImageSelector.vue @@ -17,7 +17,7 @@ accept="image/*" /> - - + + - {{ $t(title)}} +
+ {{ $t(title) }} +
+
+ {{ $t(message1) }} +
+
+ {{ $t(message2) }} +
- - {{ $t(message1)}} - - - {{ $t(message2)}} - - -
- + +
+
+
+ +
+
+ +
+
+ + class="w100 q-mt-md q-mb-sm" flat + v-close-popup rounded + @click="emit('close')" + > +
+ + {{$t('close')}} +
+
- -
- {{$t('close')}} - -
-
- +
@@ -81,5 +89,5 @@ - diff --git a/src/components/taskBlock.vue b/src/components/taskBlock.vue index b72e06c..ffc758c 100644 --- a/src/components/taskBlock.vue +++ b/src/components/taskBlock.vue @@ -8,6 +8,7 @@ rounded color="primary" class="w100 q-mt-md q-mb-xs" @click = "emit('update', newFiles)" + :disable="!(isFormValid && (isDirty(initialTask, modelValue) || newFiles.length !== 0))" > {{ $t(btnText) }}
@@ -275,6 +276,7 @@ import { useUsersStore } from 'stores/users' import { useFilesStore } from 'stores/files' import { useI18n } from 'vue-i18n' + import { isDirty } from 'helpers/helpers' import { date } from 'quasar' const { t } = useI18n() const filesStore = useFilesStore() diff --git a/src/css/app.scss b/src/css/app.scss index 6fce49f..8c0c518 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -36,7 +36,7 @@ body { } .main-content { - max-width: 600px; + max-width: var(--body-width); margin: 0 auto; } diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 74f739c..9896f9f 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -4,4 +4,4 @@ import ruRU from './ru-RU' export default { 'en-US': enUS, 'ru-RU': ruRU -}; +} diff --git a/src/i18n/ru-RU/index.ts b/src/i18n/ru-RU/index.ts index d4f7ecd..2d54ad8 100644 --- a/src/i18n/ru-RU/index.ts +++ b/src/i18n/ru-RU/index.ts @@ -1 +1 @@ -export default { EN: 'EN', RU: 'RU', '': '', error404: 'Тут ничего нет. Как вы сюда попали?', continue: 'Продолжить', back: 'Назад', close: 'Закрыть', month: 'мес.', months: 'мес.', slogan: 'Работайте вместе - это волшебство!', under_construction: 'В разработке.', B: 'Б', kB: 'КБ', MB: 'МБ', GB: 'ГБ', TB: 'ТБ', main__chats: 'Чаты', main__tasks: 'Задачи', main__meetings: 'Совещания', main__files: 'Файлы', main__users: 'Контакты', chats__search: 'Поиск', tasks__search: 'Поиск', tasks__filters: 'Фильтры', tasks__filters_by_participant: 'Типы задач', tasks__filters_to_me: 'Входящие', tasks__filters_from_me: 'Порученные', tasks__filters_observers: 'Отслеживаемые', tasks__filters_not_involved: 'Без моего участия', tasks__filters_by_priority: 'Приоритет задач', tasks__filters_priority_normal: 'Нормальный', tasks__filters_priority_important: 'Важный', tasks__filters_priority_critical: 'Критичный', tasks__filters_continue: 'Продолжить', tasks__filters_reset: 'Сбросить фильтры', tasks__show_archive: 'Показать архив', tasks__hide_archive: 'Скрыть архив', tasks__dialog_cancel_title: 'Отменить задачу?', tasks__dialog_cancel_delete: 'Удалить', tasks__dialog_cancel_ok: 'Отменить', task_create__title_card: 'Создание задачи', task_create__btn: 'Создать', task_edit__title_card: 'Редактировать задачу', task_edit__btn: 'Применить', task_view__title_card: 'Карточка задачи', task_view__go_to_chat: 'к чату', task_view__btn_cancel_task: 'Отклонить', task_view__btn_close_task: 'Выполнено', task_view__dialog_task_done_title: 'Завершить задачу', task_view__dialog_task_done_comment: 'Комментарий', task_view__dialog_task_done_btn: 'Завершить', task_view__dialog_task_cancel_title: 'Отклонить задачу', task_view__dialog_task_cancel_comment: 'Причина', task_view__dialog_task_cancel_btn: 'Отклонить', task_view__dialog_task_files: 'Файлы', task_view__error_comment: 'Необходимо указать', task_block__name: 'Заголовок', task_block__description: 'Описание', task_block__plan: 'Срок выполнения', task_block__date: 'Дата', task_block__time: 'Время', task_block__priority: 'Приоритет', task_block__priority_normal: 'Нормально', task_block__priority_important: 'Важно', task_block__priority_critical: 'Критично', task_block__attached_chat: 'Связанный чат', task_block__attach_files: 'Прикрепленные файлы', task_block__assigned_to: 'Ответственный', task_block__observers: 'Наблюдатели', task_block__attach_chat: 'Прикрепленный чат', task_block__no_chat: 'Без чата', task_block__error_name: 'Укажите название задачи', task_block__error_date: 'Некорректная дата', task_block__error_time: 'Некорректное время', task_item__from_me: 'Я', task_item__to_me: 'Мне', task_item__to_me_from_me: 'Себе', task_item__task_overdue: 'Просрочена', task_item__task_done: 'Выполнена', task_item__task_done_overdue: 'Выполнена (просрочена)', task_item__task_cancel: 'Отменена', task_priority_important: 'Важно', task_priority_critical: 'Критично', meetings__search: 'Поиск', meetings__previous: 'Прошедшие', meetings__previous_hide: 'Сбросить', meeting_create__title_card: 'Создать совещание', meeting_create__btn: 'Создать', meeting_edit__title_card: 'Редактировать совещание', meeting_edit__btn: 'Применить', meeting_view__title_card: 'Карточка совещания', meeting_view__go_to_chat: 'к чату', meeting_page__canceled: 'Отменено', meeting_page__dialog_cancel_title: 'Отменить совещание?', meeting_page__dialog_cancel_ok: 'Подтвердить', meeting_page__dialog_cancel_delete: 'Удалить', meeting_page__dialog_restore_title: 'Возобновить совещание?', meeting_page__dialog_restore_ok: 'Подтвердить', meeting_block__name: 'Тема', meeting_block__description: 'Описание', meeting_block__date: 'Дата', meeting_block__time: 'Время', meeting_block__place: 'Место', meeting_block__attach_chat: 'Прикрепленный чат', meeting_block__participants: 'Участники', meeting_block__attach_files: 'Файлы', meeting_block__error_name: 'Укажите тему совещания', meeting_block__error_date: 'Некорректная дата', meeting_block__error_time: 'Некорректное время', meeting_block__no_chat: 'Без чата', files__search: 'Поиск', files__filters: 'Фильтры', files__filters_extension: 'Расширения (типы)', files__filters_source: 'Источник ', files__filters_source_chats: 'чат', files__filters_source_tasks: 'задача', files__filters_source_meetings: 'совещание', files__filters_by: 'Автор', files__filters_size: 'Размер', files__filters_size_small: 'небольшой (менее 5МБ)', files__filters_size_middle: 'средний (5-25МБ)', files__filters_size_big: 'большой (25-100МБ)', files__filters_size_very_big: 'очень большой (более 100МБ)', files__filters_continue: 'Продолжить', files__filters_reset: 'Сбросить фильтры', header__my_projects: 'Мои проекты', header__all_projects: 'Все проекты', header__projects: 'Проекты', users__search: 'Поиск', user_card__title: 'Карточка пользователя', user_card__name: 'Имя', user_card__phone: 'Телефон', user_card__email: 'Электронная почта', user_card__position: 'Позиция', settings__title: 'Настройки', settings__language: 'Язык', settings__font_size: 'Размер шрифта', file_upload__comment: 'Загрузка только по одному файлу (Android)' } \ No newline at end of file +export default { EN: 'EN', RU: 'RU', '': '', error404: 'Тут ничего нет. Как вы сюда попали?', continue: 'Продолжить', back: 'Назад', close: 'Закрыть', month: 'мес.', months: 'мес.', slogan: 'Работайте вместе - это волшебство!', under_construction: 'В разработке.', B: 'Б', kB: 'КБ', MB: 'МБ', GB: 'ГБ', TB: 'ТБ', main__chats: 'Чаты', main__tasks: 'Задачи', main__meetings: 'Совещания', main__files: 'Файлы', main__users: 'Контакты', chats__search: 'Поиск', tasks__search: 'Поиск', tasks__filters: 'Фильтры', tasks__filters_by_participant: 'Типы задач', tasks__filters_to_me: 'Входящие', tasks__filters_from_me: 'Порученные', tasks__filters_observers: 'Отслеживаемые', tasks__filters_not_involved: 'Без моего участия', tasks__filters_by_priority: 'Приоритет задач', tasks__filters_priority_normal: 'Нормальный', tasks__filters_priority_important: 'Важный', tasks__filters_priority_critical: 'Критичный', tasks__filters_continue: 'Продолжить', tasks__filters_reset: 'Сбросить фильтры', tasks__show_archive: 'Показать архив', tasks__hide_archive: 'Скрыть архив', tasks__dialog_cancel_title: 'Отменить задачу?', tasks__dialog_cancel_delete: 'Удалить', tasks__dialog_cancel_ok: 'Отменить', task_create__title_card: 'Создание задачи', task_create__btn: 'Создать', task_edit__title_card: 'Редактировать задачу', task_edit__btn: 'Изменить', task_view__title_card: 'Карточка задачи', task_view__go_to_chat: 'к чату', task_view__btn_cancel_task: 'Отклонить', task_view__btn_close_task: 'Выполнено', task_view__dialog_task_done_title: 'Завершить задачу', task_view__dialog_task_done_comment: 'Комментарий', task_view__dialog_task_done_btn: 'Завершить', task_view__dialog_task_cancel_title: 'Отклонить задачу', task_view__dialog_task_cancel_comment: 'Причина', task_view__dialog_task_cancel_btn: 'Отклонить', task_view__dialog_task_files: 'Файлы', task_view__error_comment: 'Необходимо указать', task_block__name: 'Заголовок', task_block__description: 'Описание', task_block__plan: 'Срок выполнения', task_block__date: 'Дата', task_block__time: 'Время', task_block__priority: 'Приоритет', task_block__priority_normal: 'Нормально', task_block__priority_important: 'Важно', task_block__priority_critical: 'Критично', task_block__attached_chat: 'Связанный чат', task_block__attach_files: 'Прикрепленные файлы', task_block__assigned_to: 'Ответственный', task_block__observers: 'Наблюдатели', task_block__attach_chat: 'Прикрепленный чат', task_block__no_chat: 'Без чата', task_block__error_name: 'Укажите название задачи', task_block__error_date: 'Некорректная дата', task_block__error_time: 'Некорректное время', task_item__from_me: 'Я', task_item__to_me: 'Мне', task_item__to_me_from_me: 'Себе', task_item__task_overdue: 'Просрочена', task_item__task_done: 'Выполнена', task_item__task_done_overdue: 'Выполнена (просрочена)', task_item__task_cancel: 'Отменена', task_priority_important: 'Важно', task_priority_critical: 'Критично', meetings__search: 'Поиск', meetings__previous: 'Прошедшие', meetings__previous_hide: 'Сбросить', meeting_create__title_card: 'Создать совещание', meeting_create__btn: 'Создать', meeting_edit__title_card: 'Редактировать совещание', meeting_edit__btn: 'Изменить', meeting_view__title_card: 'Карточка совещания', meeting_view__go_to_chat: 'к чату', meeting_page__canceled: 'Отменено', meeting_page__dialog_cancel_title: 'Отменить совещание?', meeting_page__dialog_cancel_ok: 'Подтвердить', meeting_page__dialog_cancel_delete: 'Удалить', meeting_page__dialog_restore_title: 'Возобновить совещание?', meeting_page__dialog_restore_ok: 'Подтвердить', meeting_block__name: 'Тема', meeting_block__description: 'Описание', meeting_block__date: 'Дата', meeting_block__time: 'Время', meeting_block__place: 'Место', meeting_block__attach_chat: 'Прикрепленный чат', meeting_block__participants: 'Участники', meeting_block__attach_files: 'Файлы', meeting_block__error_name: 'Укажите тему совещания', meeting_block__error_date: 'Некорректная дата', meeting_block__error_time: 'Некорректное время', meeting_block__no_chat: 'Без чата', files__search: 'Поиск', files__filters: 'Фильтры', files__filters_extension: 'Расширения (типы)', files__filters_source: 'Источник ', files__filters_source_chats: 'чат', files__filters_source_tasks: 'задача', files__filters_source_meetings: 'совещание', files__filters_by: 'Автор', files__filters_size: 'Размер', files__filters_size_small: 'небольшой (менее 5МБ)', files__filters_size_middle: 'средний (5-25МБ)', files__filters_size_big: 'большой (25-100МБ)', files__filters_size_very_big: 'очень большой (более 100МБ)', files__filters_continue: 'Продолжить', files__filters_reset: 'Сбросить фильтры', header__my_projects: 'Мои проекты', header__all_projects: 'Все проекты', header__projects: 'Проекты', users__search: 'Поиск', user_card__title: 'Карточка пользователя', user_card__name: 'Имя', user_card__phone: 'Телефон', user_card__email: 'Электронная почта', user_card__position: 'Позиция', settings__title: 'Настройки', settings__language: 'Язык', settings__font_size: 'Размер шрифта', file_upload__comment: 'Загрузка только по одному файлу (Android)' } \ No newline at end of file diff --git a/src/pages/MeetingEditPage.vue b/src/pages/MeetingEditPage.vue index 86509a1..4b6e214 100644 --- a/src/pages/MeetingEditPage.vue +++ b/src/pages/MeetingEditPage.vue @@ -10,7 +10,7 @@ diff --git a/src/pages/main/TasksPage.vue b/src/pages/main/TasksPage.vue index b122081..8cedc26 100644 --- a/src/pages/main/TasksPage.vue +++ b/src/pages/main/TasksPage.vue @@ -83,36 +83,38 @@
- + { isInit.value = true } + const startRouteInfo = ref<{ id: number; taskId?: number; meetingId?: number } | null>(null) + + function setStartRouteInfo (info: { id: number; taskId?: number; meetingId?: number } | null) { + startRouteInfo.value = info + } + return { isInit, telegramUserData, + startRouteInfo, + setStartRouteInfo, init } }) diff --git a/src/stores/projects.ts b/src/stores/projects.ts index 5ec6881..2a0527e 100644 --- a/src/stores/projects.ts +++ b/src/stores/projects.ts @@ -71,12 +71,6 @@ export const useProjectsStore = defineStore('projects', () => { const getProjects = computed(() => projects.value) - const startRouteInfo = ref<{ id: number; taskId?: number; meetingId?: number } | null>(null) - - function setStartRouteInfo (info: { id: number; taskId?: number; meetingId?: number } | null) { - startRouteInfo.value = info - } - watch (currentProjectId, async (newId) => { if (newId) await initStores(); else resetStores() }, { flush: 'sync' }) @@ -91,8 +85,6 @@ export const useProjectsStore = defineStore('projects', () => { setCurrentProjectId, initStores, resetStores, - getProjects, - startRouteInfo, - setStartRouteInfo + getProjects } }) diff --git a/src/types/Task.ts b/src/types/Task.ts index 1c9df80..c977999 100644 --- a/src/types/Task.ts +++ b/src/types/Task.ts @@ -11,6 +11,7 @@ interface TaskParams { chat_id: number | null close_files: number[] close_comment: string + [key: string]: unknown } interface Task extends TaskParams {