diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..7c5d477 --- /dev/null +++ b/backend/.env @@ -0,0 +1,11 @@ +# node --env-file .env app.js +# Usage: process.env.PORT +# https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs + +PORT=3000 + +API_ID=26746106 +API_HASH=29e5f83c04e635fa583721473a6003b5 +BOT_TOKEN=7236504417:AAGVaodw3cRwGlf-jAhwnYb51OHaXcgpW8k + +BOT_SID=1AgAOMTQ5LjE1NC4xNjcuNTEBu6zLey/IpmODwaKLyTbkKwFDY28LSFPf4UZpgSKCO4bP5gGtFOGVmNDhsJxhMtUWNzhyOX46GyDliNiZ4FUQdoQ6G93DEN8mYcREljmiCp5JchNyZPmhGxl2GeclPo0tp9T/yXFUyo7PD8YpuykHH/MdWVyZxPp93Pjjpi+E03DKCwD00tEpi2TAGzW/MyQ8HUAUIK45nkJA7dnv8Up7NB9LWJ2z+8Dx81oGdVYyOHBL9qy9722LyKtvLD47KpwINjJyZOdhdBM1W8bhsGE4JkHs6DXFXOzrmMrWaE30z4corikkQoNIDL/tXttv+bJULQbbyGZvskbXuvwkV/NVen0= diff --git a/backend/_old/2025-05-03-chat.zip b/backend/_old/2025-05-03-chat.zip new file mode 100644 index 0000000..df02783 Binary files /dev/null and b/backend/_old/2025-05-03-chat.zip differ diff --git a/backend/_old/backend_v2.zip b/backend/_old/backend_v2.zip new file mode 100644 index 0000000..7e96a79 Binary files /dev/null and b/backend/_old/backend_v2.zip differ diff --git a/backend/_old/backend_v3.zip b/backend/_old/backend_v3.zip new file mode 100644 index 0000000..c4ba336 Binary files /dev/null and b/backend/_old/backend_v3.zip differ diff --git a/backend/app.bat b/backend/app.bat new file mode 100644 index 0000000..5b87ff7 --- /dev/null +++ b/backend/app.bat @@ -0,0 +1 @@ +node --env-file .env app \ No newline at end of file diff --git a/backend/app.js b/backend/app.js index f2a6b18..a0c7b9b 100644 --- a/backend/app.js +++ b/backend/app.js @@ -8,14 +8,14 @@ const bot = require('./apps/bot') const app = express() -app.use(bodyParser.json()) +app.use(bodyParser.json({limit: '10mb'})) app.use(cookieParser()) BigInt.prototype.toJSON = function () { return Number(this) } -/* app.use((req, res, next) => { +app.use((req, res, next) => { if(!(req.body instanceof Object)) return next() @@ -26,14 +26,14 @@ BigInt.prototype.toJSON = function () { .map(key => req.body[key] = escapeHtml(req.body[key])) next() -}) */ +}) app.post('(/api/admin/auth/telegram|/api/miniapp/auth)', (req, res, next) => { const data = Object.assign({}, req.query) delete data.hash const hash = req.query?.hash - const BOT_TOKEN = '7236504417:AAGVaodw3cRwGlf-jAhwnYb51OHaXcgpW8k' + const BOT_TOKEN = process.env.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') @@ -59,20 +59,19 @@ app.use('/api/miniapp', require('./apps/miniapp')) app.use((err, req, res, next) => { console.error(`Error for ${req.path}: ${err}`) + console.trace() + console.log('\n\n') let message, code [message, code = 500] = err.message.split('::') - - res.status(code).json({success: false, error: { message, code}}) + res.status(+code).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_ID || 26746106), process.env.API_HASH || '29e5f83c04e635fa583721473a6003b5', process.env.BOT_TOKEN || '7236504417:AAGVaodw3cRwGlf-jAhwnYb51OHaXcgpW8k' ) diff --git a/backend/apps/admin.js b/backend/apps/admin.js index baafc63..21cf305 100644 --- a/backend/apps/admin.js +++ b/backend/apps/admin.js @@ -1,24 +1,45 @@ const crypto = require('crypto') const express = require('express') -const multer = require('multer') const db = require('../include/db') const bot = require('./bot') const fs = require('fs') -const cookieParser = require('cookie-parser') const app = express.Router() -const upload = multer({ - storage: multer.memoryStorage(), - limits: { - fileSize: 1_000_000 // 1mb - } -}) const sessions = {} -const emailCache = {} // key = email, value = code +const cache = { + // email -> code + register: {}, + 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) => { - if (req.path == '/auth/email' || req.path == '/auth/telegram' || req.path == '/auth/register' || req.path == '/auth/logout') + 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 @@ -59,10 +80,10 @@ app.post('/auth/email', (req, res, next) => { app.post('/auth/telegram', (req, res, next) => { let customer_id = db - .prepare(`select id from customers where is_blocked = 0 and telegram_id = :telegram_id`) + .prepare(`select id from customers where telegram_id = :telegram_id`) .pluck(true) .get(res.locals) || db - .prepare(`replace into customers (telegram_id, is_blocked) values (:telegram_id, 0) returning id`) + .prepare(`replace into customers (telegram_id) values (:telegram_id) returning id`) .pluck(true) .get(res.locals) @@ -70,73 +91,198 @@ app.post('/auth/telegram', (req, res, next) => { 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}) -}) - -app.post('/auth/register', (req, res, next) => { +/* + Регистрация нового клиента выполняется за ТРИ последовательных вызова + 1. Отравляется email. Если email корректный и уже неиспользуется, то сервер возвращает ОК и на указанный email отправляется код. + 2. Отправляется email + код из письма. Если указан корректный код, то сервер отвечает ОК. + 3. Отправляется email + код из письма + желаемый пароль. Если все ОК, то сервер создает учетную запись и возвращает ОК. +*/ +app.post('/auth/email/register', (req, res, next) => { const email = String(req.body.email ?? '').trim() const code = String(req.body.code ?? '').trim() const password = String(req.body.password ?? '').trim() - - if (email) { - const validateEmail = email => 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,}))$/) - if (!validateEmail(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') - } - - if (email && !code) { - const code = Math.random().toString().substr(2, 4) - emailCache[email] = code - // To-Do: send email - console.log(`${email} => ${code}`) - } - - if (email && code && !password) { - if (emailCache[email] != code) - throw Error('INCORRECT_CODE::400') - } - - if (email && code && password) { - if (password.length < 8) - throw Error('INCORRECT_PASSWORD::400') - - db - .prepare('insert into customers (email, password, is_blocked) values (:email, :password, 0)') - .run({email, password}) - } - - res.status(200).json({success: true}) - }) + 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.register[email] = code + sendEmail(email, 'REGISTER', `${email} => ${code}`) + } + + if (stepNo == 2) { + if (cache.register[email] != code) + throw Error('INCORRECT_CODE::400') + } + + if (stepNo == 3) { + if (!checkPassword(password)) + throw Error('INCORRECT_PASSWORD::400') + + db + .prepare('insert into customers (email, password) values (:email, :password)') + .run({email, password}) + + delete cache.register[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_group_id + select id, name, email, plan, coalesce(json_balance, '{}') json_balance, coalesce(json_company, '{}') json_company, upload_chat_id from customers where id = :customer_id `) .get(res.locals) - if (row?.upload_group_id) { - row.upload_group = db - .prepare(`select id, name, telegram_id from groups where id = :group_id and project_id is null`) + 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({ group_id: row.upload_group_id}) - delete row.upload_group_id + .get({ chat_id: row.upload_chat_id}) + delete row.upload_chat_id } for (const key in row) { @@ -167,15 +313,36 @@ app.put('/customer/profile', (req, res, next) => { 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 app.get('/project', (req, res, next) => { const where = req.query.id ? ' and id = ' + parseInt(req.query.id) : '' const rows = db .prepare(` - select id, name, description, logo - from projects - where customer_id = :customer_id ${where} and is_deleted <> 1 + 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 ${where} order by name `) .all(res.locals) @@ -204,7 +371,7 @@ app.post('/project', (req, res, next) => { .pluck(true) .get(res.locals) - res.status(200).json({success: true, data: id}) + res.redirect(req.baseUrl + `/project?id=${id}`) }) app.put('/project/:pid(\\d+)', (req, res, next) => { @@ -212,11 +379,12 @@ app.put('/project/:pid(\\d+)', (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 = req.body?.is_logo_bg const info = db .prepareUpdate( 'projects', - ['name', 'description', 'logo'], + ['name', 'description', 'logo', 'is_logo_bg'], res.locals, ['id', 'customer_id']) .run(res.locals) @@ -224,39 +392,41 @@ app.put('/project/:pid(\\d+)', (req, res, next) => { if (info.changes == 0) throw Error('NOT_FOUND::404') - res.status(200).json({success: true}) + res.redirect(req.baseUrl + `/project?id=${req.params.pid}`) }) -app.delete('/project/:pid(\\d+)', async (req, res, next) => { +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 id_deleted = 1 where id = :id and customer_id = :customer_id') + .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('NOT_FOUND::404') + throw Error('BAD_REQUEST::400') - const groupIds = db - .prepare(`select id from groups where project_id = :id`) + const chatIds = db + .prepare(`select id from chats where project_id = :id`) .pluck(true) .all(res.locals) - for (const groupId of groupIds) { - await bot.sendMessage(groupId, 'Проект удален') - await bot.leaveGroup(groupId) + for (const chatId of chatIds) { + await bot.sendMessage(chatId, res.locals.is_archived ? 'Проект помещен в архив. Отслеживание сообщений прекращено.' : 'Проект восстановлен из архива.') } - db.prepare(`updates groups set project_id = null where id in (${ groupIds.join(', ')})`).run() - - res.status(200).json({success: true}) + 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) const row = db - .prepare('select 1 from projects where id = :project_id and customer_id = :customer_id and is_deleted <> 1') + .prepare('select 1 from projects where id = :project_id and customer_id = :customer_id and is_archived <> 1') .get(res.locals) if (!row) @@ -277,8 +447,8 @@ app.get('/project/:pid(\\d+)/user', (req, res, next) => { left join user_details ud on u.id = ud.user_id and ud.project_id = :project_id where id in ( select user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) + from chat_users + where chat_id in (select id from chats where project_id = :project_id) ) ${where} `) .safeIntegers(true) @@ -337,7 +507,7 @@ app.get('/project/:pid(\\d+)/company', (req, res, next) => { const rows = db .prepare(` select id, name, email, phone, description, logo, - (select json_group_array(user_id) from company_users where company_id = c.id) users + (select json_chat_array(user_id) from company_users where company_id = c.id) users from companies c where project_id = :project_id ${where} order by name @@ -373,7 +543,7 @@ app.post('/project/:pid(\\d+)/company', (req, res, next) => { .pluck(res.locals) .get(res.locals) - res.status(200).json({success: true, data: id}) + res.redirect(req.baseUrl + `/project/${req.params.pid}/company?id=${id}`) }) app.put('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { @@ -395,7 +565,7 @@ app.put('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { if (info.changes == 0) throw Error('NOT_FOUND::404') - res.status(200).json({success: true}) + res.redirect(req.baseUrl + `/project/${req.params.pid}/company?id=${req.params.cid}`) }) app.delete('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { @@ -411,13 +581,13 @@ app.delete('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => { res.status(200).json({success: true}) }) -app.get('/project/:pid(\\d+)/group', (req, res, next) => { +app.get('/project/:pid(\\d+)/chat', (req, res, next) => { const where = req.query.id ? ' and id = ' + parseInt(req.query.id) : '' const rows = db .prepare(` select id, name, telegram_id, is_channel, user_count, bot_can_ban - from groups + from chats where project_id = :project_id ${where} `) .all(res.locals) @@ -428,20 +598,20 @@ app.get('/project/:pid(\\d+)/group', (req, res, next) => { 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.uid}`) +app.get('/project/:pid(\\d+)/chat/:gid(\\d+)', (req, res, next) => { + res.redirect(req.baseUrl + `/project/${req.params.pid}/chat?id=${req.params.uid}`) }) -app.delete('/project/:pid(\\d+)/group/:gid(\\d+)', async (req, res, next) => { - res.locals.group_id = parseInt(req.params.gid) +app.delete('/project/:pid(\\d+)/chat/:gid(\\d+)', async (req, res, next) => { + res.locals.chat_id = parseInt(req.params.gid) const info = db - .prepare(`update groups set project_id = null where id = :group_id and project_id = :project_id`) + .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.group_id, 'Группа удалена из проекта') + await bot.sendMessage(res.locals.chat_id, 'Чат удален из проекта') res.status(200).json({success: true}) }) @@ -463,8 +633,8 @@ app.put('/project/:pid(\\d+)/company/:cid(\\d+)/user', (req, res, next) => { let rows = db .prepare(` select user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) + from chat_users + where chat_id in (select id from chats where project_id = :project_id) `) .pluck(true) // .raw? .get(res.locals) diff --git a/backend/apps/bot.js b/backend/apps/bot.js index 17c9c71..73b6621 100644 --- a/backend/apps/bot.js +++ b/backend/apps/bot.js @@ -10,9 +10,7 @@ const { NewMessage } = require('telegram/events') const { Button } = require('telegram/tl/custom/button') const { CustomFile } = require('telegram/client/uploads') -//const session = new StringSession('1AgAOMTQ5LjE1NC4xNjcuNTABu2OaFuD5Oyi5wGck+n5ldAfshzYfwlWee+OUxYBvFzlKAdW11Hsndu1SJBLUnKjP8sTJEPbLwdqANBhBXmQMghLVAblwK6TxLfsWxy2zf/HGLeNXohhrsep0hBxu9imyHV6OI6gQG+c5qaGkzjZrz0AcS4ut0xy99XrXgjiNfnjeMX7a0mOk6IK9iKdwbX9kXTfclFLVppiBGXolYJjVb2E57tk4+7RncIVyw+Fxn0NZfnhEfHJZly6j03arZOeM5VYl9ul8+3lJDD+KJJHeMgImmYjmcFcF3CbtkhPuTSPnWKtCnm2sRzepn5VFfoG6zgYff04fBdKGvHAai+wQSOY=') -const session = new StringSession('1AgAOMTQ5LjE1NC4xNjcuNTEBuzSgmBQR5/m8M8cyOnsLCIOkYQJTizJoJRZiPKK+eBjMuodc0JuKQwzeWBRJI/c6YxaBHvokpngf5kr57uly+meSPPlFq6MyoSSQDbEJ3VAAWJu+/ALN0ickE92RjRfM5Kw6DimC9FXuMgJJsoUHtk/i+ZGXy9JB+q67G0yy8NvFIuWpFHJDkwmi0qTlTgJ5UOm4PYkV01iNUcV5siaWFVTTLsetHtBUdMOzg5WjjvuOyYV/MIx+z7ynhvF3DxLPCugxqhCvZ/RW+0vldrTX5TZ0BzIDk2eNFQjRORJcZo6upwvH7aZYStV4DxhIi1dEYu5gyvnt4vkbR5kuvE/GqO0=') - +const session = new StringSession(process.env.BOT_SID || '') let client @@ -82,38 +80,38 @@ async function updateUserPhoto (userId, data) { .run({ user_id: userId, photo_id: photoId, photo: file.toString('base64') }) } -async function registerGroup (telegramId, isChannel) { +async function registerChat (telegramId, isChannel) { db - .prepare(`insert or ignore into groups (telegram_id, is_channel) values (:telegram_id, :is_channel)`) + .prepare(`insert or ignore into chats (telegram_id, is_channel) values (:telegram_id, :is_channel)`) .safeIntegers(true) .run({ telegram_id: telegramId, is_channel: +isChannel }) const row = db - .prepare(`select id, name from groups where telegram_id = :telegram_id`) + .prepare(`select id, name from chats where telegram_id = :telegram_id`) .safeIntegers(true) .get({telegram_id: telegramId}) if (!row?.name) { const entity = isChannel ? { channelId: telegramId } : { chatId: telegramId } - const group = await client.getEntity(isChannel ? new Api.PeerChannel(entity) : new Api.PeerChat(entity)) + const chat = await client.getEntity(isChannel ? new Api.PeerChannel(entity) : new Api.PeerChat(entity)) db - .prepare(`update groups set name = :name where id = :group_id`) - .run({ group_id: row.id, name: group.title }) + .prepare(`update chats set name = :name where id = :chat_id`) + .run({ chat_id: row.id, name: chat.title }) } return row.id } -async function attachGroup(groupId, isChannel, projectId) { +async function attachChat(chatId, isChannel, projectId) { const info = db - .prepare(`update groups set project_id = :project_id where id = :group_id and coalesce(project_id, 1) = 1`) - .run({ group_id: groupId, project_id: projectId }) + .prepare(`update chats set project_id = :project_id where id = :chat_id and coalesce(project_id, 1) = 1`) + .run({ chat_id: chatId, project_id: projectId }) if (info.changes == 1) { const inputPeer = isChannel ? - new Api.InputPeerChannel({ channelId: tgGroupId }) : - new Api.InputPeerChat({ chatlId: tgGroupId }) + new Api.InputPeerChannel({ channelId: tgChatId }) : + new Api.InputPeerChat({ chatId: tgChatId }) const query = `select (select name from customers where id = p.customer_id) || ' >> ' || p.name from projects p where id = :project_id` const message = db @@ -127,14 +125,14 @@ async function attachGroup(groupId, isChannel, projectId) { return info.changes == 1 } -async function onGroupAttach (tgGroupId, isChannel) { +async function onChatAttach (tgChatId, isChannel) { const projectId = db - .prepare(`select project_id from groups where telegram_id = :telegram_id`) + .prepare(`select project_id from chats where telegram_id = :telegram_id`) .safeIntegers(true) .pluck(true) - .get({ telegram_id: tgGroupId }) + .get({ telegram_id: tgChatId }) - const entity = isChannel ? { channelId: tgGroupId } : { chatId: tgGroupId } + const entity = isChannel ? { channelId: tgChatId } : { chatId: tgChatId } const inputPeer = await client.getEntity( isChannel ? new Api.InputPeerChannel(entity) : new Api.InputPeerChat(entity) @@ -151,48 +149,44 @@ async function onGroupAttach (tgGroupId, isChannel) { unpin: false })) -//fs.appendFileSync('./1.log', '\n>' + tgGroupId + ':' + isChannel + '<\n') +//fs.appendFileSync('./1.log', '\n>' + tgChatId + ':' + isChannel + '<\n') } -async function reloadGroupUsers(groupId, onlyReset) { +async function reloadChatUsers(chatId, onlyReset) { db - .prepare(`delete from group_users where group_id = :group_id`) - .run({ group_id: groupId }) + .prepare(`delete from chat_users where chat_id = :chat_id`) + .run({ chat_id: chatId }) if (onlyReset) return - const group = db - .prepare(`select telegram_id, is_channel, access_hash from groups where id = :group_id`) - .get({ group_id: groupId}) + const chat = db + .prepare(`select telegram_id, is_channel, access_hash from chats where id = :chat_id`) + .get({ chat_id: chatId}) - console.log (123, group) - - if (!group) + if (!chat) return - const tgGroupId = group.telegram_id - const isChannel = group.is_channel - let accessHash = group.access_hash - - console.log ('HERE') + const tgChatId = chat.telegram_id + const isChannel = chat.is_channel + let accessHash = chat.access_hash db - .prepare(`update groups set access_hash = :access_hash where id = :group_id`) + .prepare(`update chats set access_hash = :access_hash where id = :chat_id`) .safeIntegers(true) .run({ - group_id: groupId, + chat_id: chatId, access_hash: accessHash, }) const result = isChannel ? await client.invoke(new Api.channels.GetParticipants({ - channel: new Api.PeerChannel({ channelId: tgGroupId }), + channel: new Api.PeerChannel({ channelId: tgChatId }), filter: new Api.ChannelParticipantsRecent(), limit: 999999, offset: 0 })) : await client.invoke(new Api.messages.GetFullChat({ - chatId: tgGroupId, + chatId: tgChatId, })) const users = result.users.filter(user => !user.bot) @@ -202,8 +196,8 @@ async function reloadGroupUsers(groupId, onlyReset) { if (updateUser(userId, user)) { await updateUserPhoto (userId, user) - const query = `insert or ignore into group_users (group_id, user_id) values (:group_id, :user_id)` - db.prepare(query).run({ group_id: groupId, user_id: userId }) + const query = `insert or ignore into chat_users (chat_id, user_id) values (:chat_id, :user_id)` + db.prepare(query).run({ chat_id: chatId, user_id: userId }) } } } @@ -212,11 +206,11 @@ async function registerUpload(data) { if (!data.projectId || !data.media) return false - const uploadGroup = db + const uploadChat = db .prepare(` select id, telegram_id, project_id, is_channel, access_hash - from groups - where id = (select upload_group_id + from chats + where id = (select upload_chat_id from customers where id = (select customer_id from projects where id = :project_id limit 1) limit 1) @@ -225,14 +219,14 @@ async function registerUpload(data) { .safeIntegers(true) .get({project_id: data.projectId}) - if (!uploadGroup || !uploadGroup.telegram_id || uploadGroup.id == data.originGroupId) + if (!uploadChat || !uploadChat.telegram_id || uploadChat.id == data.originchatId) return false - const tgUploadGroupId = uploadGroup.telegram_id + const tgUploadChatId = uploadChat.telegram_id - const peer = uploadGroup.is_channel ? - new Api.PeerChannel({ channelId: tgUploadGroupId }) : - new Api.PeerChat({ chatlId: tgUploadGroupId }) + const peer = uploadChat.is_channel ? + new Api.PeerChannel({ channelId: tgUploadChatId }) : + new Api.PeerChat({ chatlId: tgUploadChatId }) let resultId = 0 @@ -248,16 +242,16 @@ async function registerUpload(data) { const update = result.updates.find(u => (u.className == 'UpdateNewMessage' || u.className == 'UpdateNewChannelMessage') && u.message.className == 'Message' && - (u.message.peerId.channelId?.value == tgUploadGroupId || u.message.peerId.chatId?.value == tgUploadGroupId) && + (u.message.peerId.channelId?.value == tgUploadChatId || u.message.peerId.chatId?.value == tgUploadChatId) && u.message.media) const udoc = update?.message?.media?.document if (udoc) { resultId = db .prepare(` - insert into documents (project_id, origin_group_id, origin_message_id, group_id, message_id, + insert into documents (project_id, origin_chat_id, origin_message_id, chat_id, message_id, file_id, access_hash, filename, mime, caption, size, published_by, parent_type, parent_id) - values (:project_id, :origin_group_id, :origin_message_id, :group_id, :message_id, + values (:project_id, :origin_chat_id, :origin_message_id, :chat_id, :message_id, :file_id, :access_hash, :filename, :mime, :caption, :size, :published_by, :parent_type, :parent_id) returning id `) @@ -265,9 +259,9 @@ async function registerUpload(data) { .pluck(true) .get({ project_id: data.projectId, - origin_group_id: data.originGroupId, + origin_chat_id: data.originchatId, origin_message_id: data.originMessageId, - group_id: uploadGroup.id, + chat_id: uploadChat.id, message_id: update.message.id, file_id: udoc.id.value, filename: udoc.attributes.find(attr => attr.className == 'DocumentAttributeFilename')?.fileName, @@ -291,14 +285,14 @@ async function registerUpload(data) { async function onNewServiceMessage (msg, isChannel) { const action = msg.action || {} - const tgGroupId = isChannel ? msg.peerId?.channelId?.value : msg.peerId?.chatId?.value - const groupId = await registerGroup(tgGroupId, isChannel) + const tgChatId = isChannel ? msg.peerId?.channelId?.value : msg.peerId?.chatId?.value + const chatId = await registerChat(tgChatId, isChannel) - // Group/Channel rename + // Ghat rename if (action.className == 'MessageActionChatEditTitle') { const info = db .prepare(` - update groups + update chats set name = :name, is_channel = :is_channel, last_update_time = :last_update_time where telegram_id = :telegram_id `) @@ -307,7 +301,7 @@ async function onNewServiceMessage (msg, isChannel) { name: action.title, is_channel: +isChannel, last_update_time: Math.floor (Date.now() / 1000), - telegram_id: tgGroupId + telegram_id: tgChatId }) } @@ -315,7 +309,7 @@ async function onNewServiceMessage (msg, isChannel) { if (action.className == 'MessageActionChatMigrateTo') { const info = db .prepare(` - update groups + 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 `) @@ -323,7 +317,7 @@ async function onNewServiceMessage (msg, isChannel) { .run({ name: action.title, last_update_time: Date.now() / 1000, - old_telegram_id: tgGroupId, + old_telegram_id: tgChatId, new_telegram_id: action.channelId.value }) } @@ -352,27 +346,27 @@ async function onNewServiceMessage (msg, isChannel) { } const query = isAdd ? - `insert or ignore into group_users (group_id, user_id) values (:group_id, :user_id)` : - `delete from group_users where group_id = :group_id and user_id = :user_id` - db.prepare(query).run({ group_id: groupId, user_id: userId }) + `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 tgGroupId = isChannel ? msg.peerId?.channelId?.value : msg.peerId?.chatId?.value - const groupId = await registerGroup(tgGroupId, 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 groups where telegram_id = :telegram_id`) + .prepare(`select project_id from chats where telegram_id = :telegram_id`) .safeIntegers(true) .pluck(true) - .get({telegram_id: tgGroupId}) + .get({telegram_id: tgChatId}) const media = new Api.InputMediaDocument({ id: new Api.InputDocument({ @@ -386,7 +380,7 @@ async function onNewMessage (msg, isChannel) { projectId, media, caption: msg.message, - originGroupId: groupId, + originchatId: chatId, originMessageId: msg.id, parentType: 0, publishedBy: registerUser (msg.fromId?.userId?.value) @@ -399,22 +393,22 @@ async function onNewMessage (msg, isChannel) { select name from projects where id in ( - select project_id from groups where id = :group_id + select project_id from chats where id = :chat_id union - select id from projects where upload_group_id = :group_id) + select id from projects where upload_chat_id = :chat_id) `) .pluck(true) - .get({ group_id: groupId }) + .get({ chat_id: chatId }) if (projectName) - return await bot.sendMessage(groupId, 'Группа уже используется на проекте ' + projectName) + return await bot.sendMessage(chatId, 'Группа уже используется на проекте ' + projectName) const [_, time64, key] = msg.message.substr(3).split('-') const now = Math.floor(Date.now() / 1000) const time = Buffer.from(time64, 'base64') if (now - 3600 >= time && time >= now) - return await bot.sendMessage(groupId, 'Время действия ключа для привязки истекло') + return await bot.sendMessage(chatId, 'Время действия ключа для привязки истекло') const projectId = db .prepare(`select id from projects where generate_key(id, :time) = :key`) @@ -422,8 +416,8 @@ async function onNewMessage (msg, isChannel) { .get({ key: msg.message.trim(), time }) if (projectId) { - await attachGroup(groupId, isChannel, projectId) - await onGroupAttach(tgGroupId, isChannel) + await attachChat(chatId, isChannel, projectId) + await onChatAttach(tgChatId, isChannel) } } @@ -440,12 +434,12 @@ async function onNewMessage (msg, isChannel) { db .prepare(` update customers - set upload_group_id = :group_id + set upload_chat_id = :chat_id where id = :customer_id and telegram_user_id = :telegram_user_id `) .safeIntegers(true) .run({ - group_id: groupId, + chat_id: chatId, customer_id: customerId, telegram_user_id: tgUserId }) @@ -462,9 +456,9 @@ async function onNewMessage (msg, isChannel) { db .prepare(` - update groups + update chats set project_id = :project_id - where id = :group_id and exists( + where id = :chat_id and exists( select 1 from customers where id = :customer_id and telegram_user_id = :telegram_user_id) @@ -472,13 +466,13 @@ async function onNewMessage (msg, isChannel) { .safeIntegers(true) .run({ project_id: projectId, - group_id: groupId, + chat_id: chatId, customer_id: customerId, telegram_user_id: tgUserId }) - await reloadGroupUsers(groupId, false) - await onGroupAttach(tgGroupId, isChannel) + await reloadChatUsers(chatId, false) + await onChatAttach(tgChatId, isChannel) } } } @@ -516,34 +510,34 @@ async function onNewUserMessage (msg) { } async function onUpdatePaticipant (update, isChannel) { - const tgGroupId = isChannel ? update.channelId?.value : update.chatlId?.value - if (!tgGroupId || update.userId?.value != bot.id) + const tgChatId = isChannel ? update.channelId?.value : update.chatlId?.value + if (!tgChatId || update.userId?.value != bot.id) return - const groupId = await registerGroup (tgGroupId, isChannel) + 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 reloadGroupUsers(groupId, isBan) + await reloadChatUsers(chatId, isBan) if (isBan) { db - .prepare(`update groups set project_id = null where id = :group_id`) - .run({group_id: groupId}) + .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 groups set bot_can_ban = :bot_can_ban where id = :group_id`) - .run({group_id: groupId, bot_can_ban: +botCanBan}) + .prepare(`update chats set bot_can_ban = :bot_can_ban where id = :chat_id`) + .run({chat_id: chatId, bot_can_ban: +botCanBan}) } class Bot extends EventEmitter { async start (apiId, apiHash, botAuthToken) { - this.id = 7236504417n + this.id = BigInt(botAuthToken.split(':')[0]) client = new TelegramClient(session, apiId, apiHash, {}) @@ -568,7 +562,7 @@ class Bot extends EventEmitter { }) await client.start({botAuthToken}) - console.log('SID: ', session.save()) + console.log('BOT_SID: ', session.save()) } async uploadDocument(projectId, fileName, mime, data, parentType, parentId, publishedBy) { @@ -616,20 +610,20 @@ class Bot extends EventEmitter { } } - async reloadGroupUsers(groupId, onlyReset) { - return reloadGroupUsers(groupId, onlyReset) + async reloadChatUsers(chatId, onlyReset) { + return reloadChatUsers(chatId, onlyReset) } - async sendMessage (groupId, message) { - const group = db - .prepare(`select telegram_id, is_channel from groups where id = :group_id`) - .get({ group_id: groupId}) + async sendMessage (chatId, message) { + const chat = db + .prepare(`select telegram_id, is_channel from chats where id = :chat_id`) + .get({ chat_id: chatId}) - if (!group) + if (!chat) return - const entity = group.is_channel ? { channelId: group.telegram_id } : { chatId: group.telegram_id } - const inputPeer = await client.getEntity( group.is_channel ? + 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) ) @@ -640,19 +634,19 @@ class Bot extends EventEmitter { await delay(1000) } - async leaveGroup (groupId) { - const group = db - .prepare(`select telegram_id, is_channel from groups where id = :group_id`) - .get({ group_id: groupId}) + async leaveChat (chatId) { + const chat = db + .prepare(`select telegram_id, is_channel from chats where id = :chat_id`) + .get({ chat_id: chatId}) - if (!group) + if (!chat) return - if (group.is_channel) { - const inputPeer = await client.getEntity(new Api.InputPeerChannel({ channelId: group.telegram_id })) + if (chat.is_channel) { + const inputPeer = await client.getEntity(new Api.InputPeerChannel({ channelId: chat.telegram_id })) await client.invoke(new Api.channels.LeaveChannel({ channel: inputPeer })) } else { - await client.invoke(new Api.messages.DeleteChatUser({ chatId: group.telegram_id, userId: this.id })) + await client.invoke(new Api.messages.DeleteChatUser({ chatId: chat.telegram_id, userId: this.id })) } } } diff --git a/backend/apps/miniapp.js b/backend/apps/miniapp.js index 7a72498..0968b4d 100644 --- a/backend/apps/miniapp.js +++ b/backend/apps/miniapp.js @@ -19,9 +19,9 @@ function hasAccess(project_id, user_id) { return !!db .prepare(` select 1 - from group_users + from chat_users where user_id = :user_id and - group_id in (select id from groups where project_id = :project_id) and + chat_id in (select id from chats 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) `) @@ -70,13 +70,13 @@ app.get('/project', (req, res, next) => { const rows = db .prepare(` select p.id, p.name, p.description, p.logo, - c.name customer_name, c.upload_group_id <> 0 has_upload + c.name customer_name, c.upload_chat_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) + from chats + where id in (select chat_id from chat_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 `) @@ -114,9 +114,9 @@ app.get('/project/:pid(\\d+)/user', (req, res, next) => { .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) + from chat_users + where chat_id in (select id from chats where project_id = :project_id) + and chat_id in (select chat_id from chat_users where user_id = :user_id) ), contributors (user_id) as ( select created_by from tasks where project_id = :project_id @@ -193,29 +193,29 @@ app.get('/project/:pid(\\d+)/user', (req, res, next) => { }) app.get('/project/:pid(\\d+)/user/reload', async (req, res, next) => { - const groupIds = db - .prepare(`select id from groups where project_id = :project_id`) + const chatIds = db + .prepare(`select id from chats 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) + for (const chatId of chatIds) { + await bot.reloadGroupUsers(chatId) await sleep(1000) } res.status(200).json({success: true}) }) -app.get('/project/:pid(\\d+)/group', (req, res, next) => { +app.get('/project/:pid(\\d+)/chat', (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) + from chats + where project_id = :project_id and id in (select chat_id from chat_users where user_id = :user_id) ${where} `) .all(res.locals) @@ -226,8 +226,8 @@ app.get('/project/:pid(\\d+)/group', (req, res, next) => { 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}`) +app.get('/project/:pid(\\d+)/chat/:gid(\\d+)', (req, res, next) => { + res.redirect(req.baseUrl + `/project/${req.params.pid}/chat?id=${req.params.gid}`) }) // TASK @@ -237,8 +237,8 @@ app.get('/project/:pid(\\d+)/task', (req, res, next) => { 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 + (select json_chat_array(user_id) from task_users where task_id = t.id) observers, + (select json_chat_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)) @@ -351,8 +351,8 @@ app.put('/project/:pid(\\d+)/task/:tid(\\d+)/observer', (req, res, next) => { let rows = db .prepare(` select user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) + from chat_users + where chat_id in (select id from chats where project_id = :project_id) `) .pluck(true) .all(res.locals) @@ -379,8 +379,8 @@ app.get('/project/:pid(\\d+)/meeting', (req, res, next) => { 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 + (select json_chat_array(user_id) from meeting_users where meeting_id = m.id) participants, + (select json_chat_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)) @@ -483,8 +483,8 @@ app.put('/project/:pid(\\d+)/meeting/:mid(\\d+)/participants', (req, res, next) let rows = db .prepare(` select user_id - from group_users - where group_id in (select id from groups where project_id = :project_id) + from chat_users + where chat_id in (select id from chats where project_id = :project_id) `) .pluck(true) // .raw? .all(res.locals) @@ -516,10 +516,10 @@ app.get('/project/:pid(\\d+)/document', (req, res, next) => { // 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 + select id, origin_chat_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) + origin_chat_id in (select chat_id from chat_users where user_id = :user_id) or parent_type = 1 and parent_id in ( select id @@ -566,9 +566,9 @@ app.use('/project/:pid(\\d+)/document/:did(\\d+)', (req, res, next) => { throw Error('NOT_FOUND::404') if (doc.parent_type == 0) { - res.locals.group_id = doc.group_id + res.locals.chat_id = doc.chat_id const row = db - .prepare(`select 1 from group_users where group_id = :group_id and user_id = :user_id`) + .prepare(`select 1 from chat_users where chat_id = :chat_id and user_id = :user_id`) .get(res.locals) if (row) { diff --git a/backend/data/db.sqlite b/backend/data/db.sqlite index 9ad2084..c67578d 100644 Binary files a/backend/data/db.sqlite and b/backend/data/db.sqlite differ diff --git a/backend/data/db.sqlite-shm b/backend/data/db.sqlite-shm deleted file mode 100644 index 41f2610..0000000 Binary files a/backend/data/db.sqlite-shm and /dev/null differ diff --git a/backend/data/db.sqlite-wal b/backend/data/db.sqlite-wal deleted file mode 100644 index 53d4572..0000000 Binary files a/backend/data/db.sqlite-wal and /dev/null differ diff --git a/backend/data/init.sql b/backend/data/init.sql index 841b7ff..42a9e0b 100644 --- a/backend/data/init.sql +++ b/backend/data/init.sql @@ -10,9 +10,10 @@ create table if not exists customers ( json_balance text default '{}', is_blocked integer default 0, json_company text default '{}', - upload_group_id integer, + upload_chat_id integer, json_backup_server text default '{}', - json_backup_params text default '{}' + json_backup_params text default '{}', + json_settings text default '{}' ) strict; create table if not exists projects ( @@ -21,10 +22,11 @@ create table if not exists projects ( name text not null check(trim(name) <> '' and length(name) < 256), description text check(description is null or length(description) < 4096), logo text, - is_deleted integer default 0 + is_logo_bg integer default 0, + is_archived integer default 0 ) strict; -create table if not exists groups ( +create table if not exists chats ( id integer primary key autoincrement, project_id integer references projects(id) on delete cascade, name text, @@ -35,7 +37,7 @@ create table if not exists groups ( user_count integer, last_update_time integer ); -create unique index if not exists idx_groups_telegram_id on groups (telegram_id); +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, @@ -90,9 +92,9 @@ create table if not exists meetings ( create table if not exists documents ( id integer primary key autoincrement, project_id integer references projects(id) on delete cascade, - origin_group_id integer references groups(id) on delete set null, + origin_chat_id integer references chats(id) on delete set null, origin_message_id integer, - group_id integer references groups(id) on delete set null, + chat_id integer references chats(id) on delete set null, message_id integer, file_id integer, access_hash integer, @@ -142,20 +144,20 @@ create table if not exists company_users ( primary key (company_id, user_id) ) without rowid; -create table if not exists group_users ( - group_id integer references groups(id) on delete cascade, +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 (group_id, user_id) + primary key (chat_id, user_id) ) without rowid; pragma foreign_keys = on; -create trigger if not exists trg_groups_update after update -on groups +create trigger if not exists trg_chats_update after update +on chats when NEW.project_id is null begin - delete from group_users where group_id = NEW.id; + delete from chat_users where chat_id = NEW.id; end; diff --git a/backend/docs/api.xls b/backend/docs/api.xls new file mode 100644 index 0000000..06fc552 Binary files /dev/null and b/backend/docs/api.xls differ diff --git a/backend/docs/public.txt b/backend/docs/public.txt new file mode 100644 index 0000000..7f705ee --- /dev/null +++ b/backend/docs/public.txt @@ -0,0 +1,12 @@ +Описание ПО + +Программа состоит из двух частей: бота и приложение miniapp, привязанное к боту. Приложение отображает проекты для пользователя, задачи и встречи на проекте, прикрепленные к ним файлы, а также список тех, с кем пользователь пересекался в группах. Бот отслеживает не только участников группы, но и при наличии админских прав в группе, пересылаемые файлы и сообщения для резервного копирования. + +Термины +Клиенты - специальные аккаунты для организаций, регистрируемые в miniapp, позволяющие управлять проектами и контактами на них. +Пользователи - пользователи Telegram, участвующие в одной или нескольких группах где есть бот. Пользователи могут заходить в miniapp и + +После добавления бота в группу, группа привязывается к специальному внутреннему клиенту, который имеет только один проект "Вне проектов". Администратор группы может + +После добавления в группу и привязки токеном к аккаунту клиента, бот отслеживает новые сообщения в группе. При обнаружении файла он копируется с специальную группу, указанную клиентом, и регистрирует его в списке файлов, доступных для участников группы. Бот не хранит ни сами файлы, ни сообщения. + diff --git a/backend/docs/questions.txt b/backend/docs/questions.txt new file mode 100644 index 0000000..2b2fdb5 --- /dev/null +++ b/backend/docs/questions.txt @@ -0,0 +1,93 @@ +id-телеграма могут быть больше 32-битного int, поэтому лучше использовать свои id + кеш, который позволит быстро конвертировать одни id в другие. Хотя может быть использование BigInt будет достаточно. Большие id могут негативно сказаться на скорости выборки данных и индексировании. + +A: Будут использоваться свои id, а ID Telegram записиываться в отдельные колонки. +----------------------------------------------------- +По хорошему для каждого customer надо иметь свою базу. Поскольку для отображения данных проекта требуется информация только именно этого customer, то информация может быть локализвана. При этом остается общая база, в которой хранится список пользователей (таблица users) и информация, необходимая для формирования первой сводной страницы, напр. общее число незавершенных задач и календарь совещаний, а также id-клиентов, у которых пользователь участвует/вал в проектах. +? Можно ли сделать так, чтобы клиент имел свою установку, но при этом пользователи обращались к нашему боту/miniapp? +Если данные запрашивать не напрямую из базы, а по http/https, то каждый узел будет иметь свою базу. +----------------------------------------------------- +Хотелось бы не хранить сообщения локально. А запрашивать их за день и отправлять их бекап в специальную группу. Бот имеет ограничение 20-30 запросов в секунду, т.е. за минуту бот может получить данные из 1200 чатов. + +A: Не хранить сообщения. Запрашивать их, если клиент установил настройку. +----------------------------------------------------- +При удалении проекта/задачи/встречи/компании надо также чистить links. Возможно лучше сделать отдельные таблицы для каждой связки, чтобы использовать FK + delete cascade + +A: Операция выполняется DELETE CASCADE автоматически +----------------------------------------------------- +ФИО пользователей задает customer и ФИО в адресной книге всегда показывается. Телефон задает пользователь и пользователь может указать на каком проекте его показывать, а где нет. По умолчанию "Не показывать" +<- может храниться в users.is_show_phone = [project_id1, project_id3] + +A: Хранится в настойках пользователя в таблице users +----------------------------------------------------- +Если пользователь изменил задачу, то у тех, у кого она есть в миниаппе, должны получить по ней обновление. Вопрос в том, как маякнуть клиенту, что что-то обновилось? +Два варианта: клиент должен запрашивать обновления скажем раз в минуту или же использовать WebSocket. Есть вероятность, что для сервера поддерживать кучу WebScoket слишком накладно. +----------------------------------------------------- +Не делать API, который возвращает одну сущность, а только список. +Если надо сущность, то можно /api/project?id=1 +Или можно использовать redirect с /api/project/:id на /api/project?id=1 + +A: Да +----------------------------------------------------- +Кто заполняет ФИО пользователя? Если админ, тогда возможна ситуация, когда на проекте одного админа человек будет с ФИО, а на проекте другого - нет. Возможно ФИО также должен заполнять сам пользователь. По сути есть некоторая дыра, когда пользователь предоставляет ФИО, телефон и номер телеграма всем желающим, +когда можно заманить человека в группу с ботом и узнать его личные данные. + +A: Фамилию задает клиент отдельно на каждом проекте. А также роль и департамент. +----------------------------------------------------- +Польщователь сам решает показывать ли ему ФИО и/или телефон на +проектах конкретного админа системы. + +А: Пользователь решает только про телефон +----------------------------------------------------- +Добавить документы может любой из участников. Удалять можно только своё или если создатель. + +A: Да. +----------------------------------------------------- +Если переслать документ боту, то бот спросит к какой задаче прикрепить. Автор в этом случае тот, кто переслал сообщение. + +A: В пересылаем соообщении нет информации о том, откуда оно было получено, соотв. нельзя определить к какому проекту относится пересылаемое напрямую. Как вариант - использовать последний проект, где пользователь отправил сообщение в группе + кнопка Другой проект. +Если бот не имеет админских прав, то это будет плохо работать. +----------------------------------------------------- +Предположим, что бота добавляют в группу А. Что делать, если не все в группе используют бота/miniapp? +----------------------------------------------------- +Если бот не получает админских прав, то привязать группу токеном не получится, т.к. бот не видит сообщений. Можно сделать, чтобы пользователь отправил комманду /attach. Пока передать параметр в команду невозможно. +https://gram.js.org/tl/bots/SetBotCommands + + +Для отправки пользователям сообщений бот должен знать не только их id, но и accessHash. Аналогично для отправки сообщения в чат или скачивания файла, т.е. надо хранить еще и это поле. +Когда пользователь отписывается от бота, то надо обнулять его hash. В адресной книге тех, к кому у бота нет доступа, т.е. они участники группы, но у них нет бота, отображать специальным образом. + +А: если у того, кому назначили задачу, нет бота и следовательно accessHash, то можно отправлять в один из чатов, в котором участвуют создатель и ответственный, возможно отсортировав по времени последнего сообщения от каждого из них. Наблюдатели должны использовать бота. +----------------------------------------------------- +Возможно дать некоторым пользователям добавлять группы к проекту самостоятельно, путем нажатия кнопки "Добавить группу". +----------------------------------------------------- +В uTasks поначалу отображали только задачи, связанные с пользователем, потом они добавили крыж "Показывать все задачи" по просьбам пользователей. Почему пользователям это понадобилось? Возможно потому что у них нет механизма наблюдателей. +Возможно нужно для контроля начальником => отдельное приложение +----------------------------------------------------- +Предположим, что бот был в группе, а потом его удалили из нее. +Должна ли группа отображаться в списке групп проекта? +----------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/docs/telegram.txt b/backend/docs/telegram.txt new file mode 100644 index 0000000..49421dd --- /dev/null +++ b/backend/docs/telegram.txt @@ -0,0 +1,69 @@ +const { Api, TelegramClient } = require('telegram') +const { StringSession } = require('telegram/sessions') +const session = new StringSession('') + +const apiId = 26746106 +const apiHash = '29e5f83c04e635fa583721473a6003b5' +const BOT_TOKEN = '7236504417:AAGVaodw3cRwGlf-jAhwnYb51OHaXcgpW8k'; + +const client = new TelegramClient(session, apiId, apiHash, {}) + +(async function run() { + await client.start({botAuthToken: BOT_TOKEN}) + ... +})() + +Получение информации о себе +const result = await client.getEntity("me") + +Группа при создании имеет тип group. Её id выглядит как -4646437202 +Для получения информации надо отбросить минус +const result = await client.getEntity(new Api.PeerChat({ chatId: 4646437202n })) +Для получения пользователей и описания +const result = await client.invoke(new Api.messages.GetFullChat({chatId: 4646437202n})) + +После включения топиков группа меняется на канал и её id становится вида -1002496664184 +Для получения информации +const result = await client.getEntity(new Api.PeerChannel({ channelId: -1002496664184n })) +При этом аттрибут forum = true +Если топики отключить, то id и класс уже не меняется, но forum = false +Для получения списка пользователей канала +const channel = new Api.PeerChannel({ channelId: -1002496664184n }) +const result = await client.invoke( + new Api.channels.GetParticipants({ + channel, + filter: new Api.ChannelParticipantsRecent(), + limit: 999999, + offset:0 , + }) +) + +Скачивание файла из сообщения на диск +client.addEventHandler(async (update) => { + const msg = update.message + if (msg?.className == 'Message') { + const buffer = await client.downloadMedia(msg, {}) + fs.writeFileSync("d:/CODES/Telegram/tmp/file", buffer) + } + +}, new NewMessage({})); + +Примеры кода +Получение списка пользователей в супергруппе +https://gist.github.com/waptik/9de410055eac8a60668ce7ac1e5183ac + +Ссылка для выбора группы - https://qna.habr.com/q/1374460 + +У пользователя есть Name = First Name + Last Name - обязательно, а также username - опционально для t.me/username + +Выбор куда отправить - ForwardTo - https://core.telegram.org/widgets/share + +Размеры превьюшек - https://core.telegram.org/api/files#stripped-thumbnails + +you can just pass an ID. +if channel/superroup => add -100 +if group add => - +if user don't add anything +for example for your test group2 it would be +client.sendMessage(-463172658,{params}) +https://github.com/gram-js/gramjs/issues/146#issuecomment-913528553 \ No newline at end of file diff --git a/backend/letsgo.bat b/backend/letsgo.bat deleted file mode 100644 index d831440..0000000 --- a/backend/letsgo.bat +++ /dev/null @@ -1 +0,0 @@ -node app \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index 26bb085..be034e7 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1134,9 +1134,9 @@ "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==", + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", "license": "MIT", "dependencies": { "semver": "^7.3.5" diff --git a/backend/public/index.html b/backend/public/index.html deleted file mode 100644 index df4399f..0000000 --- a/backend/public/index.html +++ /dev/null @@ -1,339 +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 5ee0dd9..bc319e3 100644 Binary files a/i18n-2.xlsm and b/i18n-2.xlsm differ diff --git a/package-lock.json b/package-lock.json index cbf24a2..ab95073 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,10 @@ "dependencies": { "@quasar/cli": "^2.5.0", "@quasar/extras": "^1.16.4", + "@quasar/vite-plugin": "^1.9.0", "axios": "^1.2.1", "pinia": "^2.0.11", - "quasar": "^2.16.0", + "quasar": "^2.18.1", "vue": "^3.4.18", "vue-i18n": "^9.2.2", "vue-router": "^4.0.12" @@ -25,7 +26,6 @@ "@twa-dev/types": "^8.0.2", "@types/node": "^20.17.30", "@types/telegram-web-app": "^7.10.1", - "@vue/devtools": "^7.7.2", "@vue/eslint-config-typescript": "^14.1.3", "autoprefixer": "^10.4.2", "eslint": "^9.14.0", @@ -108,233 +108,9 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.3.tgz", "integrity": "sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==", - "dev": true, + "devOptional": true, "license": "(Apache-2.0 AND BSD-3-Clause)" }, - "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/@electron/get/node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@electron/get/node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/get/node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/@electron/get/node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron/get/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@electron/get/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@electron/get/node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/@electron/get/node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/@electron/get/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/get/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron/get/node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@electron/get/node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron/get/node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@electron/get/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@electron/get/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", @@ -342,7 +118,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -359,7 +134,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -376,7 +150,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -393,7 +166,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -410,7 +182,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -427,7 +198,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -444,7 +214,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -461,7 +230,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -478,7 +246,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -495,7 +262,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -512,7 +278,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -529,7 +294,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -546,7 +310,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -563,7 +326,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -580,7 +342,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -597,7 +358,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -614,7 +374,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -631,7 +390,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -648,7 +406,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -665,7 +422,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -682,7 +438,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -699,7 +454,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -716,7 +470,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -733,7 +486,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -750,7 +502,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1233,7 +984,7 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -1248,7 +999,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1258,7 +1009,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1268,7 +1019,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1285,7 +1036,7 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1708,7 +1459,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/@quasar/vite-plugin/-/vite-plugin-1.9.0.tgz", "integrity": "sha512-r1MFtI2QZJ2g20pe75Zuv4aoi0uoK8oP0yEdzLWRoOLCbhtf2+StJpUza9TydYi3KcvCl9+4HUf3OAWVKoxDmQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1754,7 +1504,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1768,7 +1517,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1782,7 +1530,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1796,7 +1543,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1810,7 +1556,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1824,7 +1569,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1838,7 +1582,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1852,7 +1595,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1866,7 +1608,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1880,7 +1621,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1894,7 +1634,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1908,7 +1647,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1922,7 +1660,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1936,7 +1673,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1950,7 +1686,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1964,7 +1699,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1978,7 +1712,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1992,7 +1725,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2006,7 +1738,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2020,13 +1751,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true, - "license": "MIT" - }, "node_modules/@sindresorhus/is": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", @@ -2039,26 +1763,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, - "license": "MIT" - }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2089,19 +1793,6 @@ "@types/node": "*" } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, "node_modules/@types/chrome": { "version": "0.0.262", "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.262.tgz", @@ -2140,21 +1831,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -2243,16 +1923,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -2292,16 +1962,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -2332,17 +1992,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", @@ -2540,7 +2189,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", - "dev": true, "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" @@ -2640,245 +2288,12 @@ "he": "^1.2.0" } }, - "node_modules/@vue/devtools": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools/-/devtools-7.7.2.tgz", - "integrity": "sha512-YLGea5P5cX3av6ExnQ08cbk/BYSUyfp0frRPQQgEYVfC53QV8UVisYFVdB2eFCjsQ9b+z0LmEIGtXAmTMnKQNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-electron": "^7.7.2", - "@vue/devtools-kit": "^7.7.2" - }, - "bin": { - "vue-devtools": "cli.mjs" - } - }, "node_modules/@vue/devtools-api": { "version": "6.6.4", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, - "node_modules/@vue/devtools-core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.2.tgz", - "integrity": "sha512-lexREWj1lKi91Tblr38ntSsy6CvI8ba7u+jmwh2yruib/ltLUcsIzEjCnrkh1yYGGIKXbAuYV2tOG10fGDB9OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^7.7.2", - "@vue/devtools-shared": "^7.7.2", - "mitt": "^3.0.1", - "nanoid": "^5.0.9", - "pathe": "^2.0.2", - "vite-hot-client": "^0.2.4" - }, - "peerDependencies": { - "vue": "^3.0.0" - } - }, - "node_modules/@vue/devtools-core/node_modules/nanoid": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", - "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^18 || >=20" - } - }, - "node_modules/@vue/devtools-core/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vue/devtools-electron": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-electron/-/devtools-electron-7.7.2.tgz", - "integrity": "sha512-WxCwLdqBdKDGHwEAU9BozOgPIOMwncM8lcze4fDs5HYRGaICclW9du1ARH5eewJl8wTvSGs2I0r/p5q60p6wAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-core": "^7.7.2", - "@vue/devtools-kit": "^7.7.2", - "@vue/devtools-shared": "^7.7.2", - "electron": "^32.2.6", - "execa": "^9.5.1", - "h3": "^1.13.0", - "ip": "^2.0.1", - "pathe": "^2.0.2", - "socket.io": "^4.8.1", - "socket.io-client": "^4.8.1" - } - }, - "node_modules/@vue/devtools-electron/node_modules/execa": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", - "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.3", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "human-signals": "^8.0.0", - "is-plain-obj": "^4.1.0", - "is-stream": "^4.0.1", - "npm-run-path": "^6.0.0", - "pretty-ms": "^9.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.5.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@vue/devtools-electron/node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vue/devtools-electron/node_modules/human-signals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", - "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@vue/devtools-electron/node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vue/devtools-electron/node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vue/devtools-electron/node_modules/npm-run-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vue/devtools-electron/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vue/devtools-electron/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vue/devtools-electron/node_modules/strip-final-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vue/devtools-kit": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz", - "integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-shared": "^7.7.2", - "birpc": "^0.2.19", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^1.0.0", - "speakingurl": "^14.0.1", - "superjson": "^2.2.1" - } - }, - "node_modules/@vue/devtools-shared": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz", - "integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "rfdc": "^1.4.1" - } - }, "node_modules/@vue/eslint-config-typescript": { "version": "14.3.0", "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.3.0.tgz", @@ -3019,7 +2434,7 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3454,16 +2869,6 @@ ], "license": "MIT" }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -3486,16 +2891,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/birpc": { - "version": "0.2.19", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", - "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -3569,15 +2964,6 @@ "dev": true, "license": "ISC" }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/boxen": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", @@ -3799,7 +3185,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", - "dev": true, + "devOptional": true, "license": "MIT/X11" }, "node_modules/buffer-crc32": { @@ -3816,7 +3202,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/bundle-name": { @@ -4156,29 +3542,6 @@ "node": ">=6" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone-response/node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4203,7 +3566,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -4393,35 +3756,12 @@ "node": ">= 0.6" } }, - "node_modules/cookie-es": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", - "dev": true, - "license": "MIT" - }, "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/copy-anything": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-what": "^4.1.8" - }, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4483,16 +3823,6 @@ "node": ">= 8" } }, - "node_modules/crossws": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.4.tgz", - "integrity": "sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, "node_modules/crypto-random-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", @@ -4761,13 +4091,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true, - "license": "MIT" - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4786,13 +4109,6 @@ "node": ">= 0.8" } }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "dev": true, - "license": "MIT" - }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -4803,14 +4119,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -4906,25 +4214,6 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, - "node_modules/electron": { - "version": "32.3.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.3.3.tgz", - "integrity": "sha512-7FT8tDg+MueAw8dBn5LJqDvlM4cZkKJhXfgB3w7P5gvSoUQVAY6LIQcXJxgL+vw2rIRY/b9ak7ZBFbCMF2Bk4w==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", - "extract-zip": "^2.0.1" - }, - "bin": { - "electron": "cli.js" - }, - "engines": { - "node": ">= 12.20.55" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.88", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz", @@ -4960,107 +4249,6 @@ "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.1.1" - } - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -5073,16 +4261,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/es-abstract": { "version": "1.23.9", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", @@ -5226,19 +4404,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/esbuild": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -5974,43 +5143,6 @@ "node": ">=4" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6066,16 +5198,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/fdir": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", @@ -6091,35 +5213,6 @@ } } }, - "node_modules/figures": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6352,7 +5445,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6514,25 +5606,6 @@ "node": ">= 6" } }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -6628,24 +5701,6 @@ "dev": true, "license": "MIT" }, - "node_modules/h3": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.1.tgz", - "integrity": "sha512-+ORaOBttdUm1E2Uu/obAyCguiI7MbBvsLTndc3gyK3zU+SYLoZXlyCP9Xgy0gikkGufFLTZXCXD6+4BsufnmHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.2", - "crossws": "^0.3.3", - "defu": "^6.1.4", - "destr": "^2.0.3", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.0", - "radix3": "^1.1.2", - "ufo": "^1.5.4", - "uncrypto": "^0.1.3" - } - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -6663,7 +5718,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -6760,13 +5815,6 @@ "he": "bin/he" } }, - "node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "dev": true, - "license": "MIT" - }, "node_modules/html-minifier-terser": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", @@ -6918,7 +5966,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -7010,13 +6058,6 @@ "node": ">= 0.4" } }, - "node_modules/ip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", - "dev": true, - "license": "MIT" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7026,16 +6067,6 @@ "node": ">= 0.10" } }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -7605,19 +6636,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/is-wsl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", @@ -7735,14 +6753,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC", - "optional": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -7990,20 +7000,6 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -8179,13 +7175,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, "node_modules/mlly": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", @@ -8283,13 +7272,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-mock-http": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.0.tgz", - "integrity": "sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==", - "dev": true, - "license": "MIT" - }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -8480,16 +7462,6 @@ "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==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -8684,19 +7656,6 @@ "node": ">=6" } }, - "node_modules/parse-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -8780,20 +7739,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -8923,22 +7868,6 @@ "node": ">= 0.8.0" } }, - "node_modules/pretty-ms": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", - "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-ms": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -8956,16 +7885,6 @@ "dev": true, "license": "MIT" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -8997,17 +7916,6 @@ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "license": "ISC" }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -9049,9 +7957,9 @@ } }, "node_modules/quasar": { - "version": "2.17.7", - "resolved": "https://registry.npmjs.org/quasar/-/quasar-2.17.7.tgz", - "integrity": "sha512-nPJdHoONlcW7WEU2Ody907Wx945Zfyuea/KP4LBaEn5AcL95PUWp8Gz/0zDYNnFw0aCWRtye3SUAdQl5tmrn5w==", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/quasar/-/quasar-2.18.1.tgz", + "integrity": "sha512-db/P64Mzpt1uXJ0MapaG+IYJQ9hHDb5KtTCoszwC78DR7sA+Uoj7nBW2EytwYykIExEmqavOvKrdasTvqhkgEg==", "license": "MIT", "engines": { "node": ">= 10.18.1", @@ -9103,13 +8011,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "dev": true, - "license": "MIT" - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -9446,37 +8347,10 @@ "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/rollup": { "version": "4.32.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.0.tgz", "integrity": "sha512-JmrhfQR31Q4AuNBjjAX4s+a/Pu/Q8Q9iwjWBsjRH1q52SPFE2NqRMK6fUZKKnvKO6id+h7JIRf0oYsph53eATg==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -9695,7 +8569,7 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" @@ -9800,7 +8674,7 @@ "version": "1.83.4", "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.83.4.tgz", "integrity": "sha512-Hf2burRA/y5PGxsg6jB9UpoK/xZ6g/pgrkOcdl6j+rRg1Zj8XhGKZ1MTysZGtTPUUmiiErqzkP5+Kzp95yv9GQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^2.0.0", @@ -9848,7 +8722,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9865,7 +8738,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9882,7 +8754,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9899,7 +8770,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9916,7 +8786,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9933,7 +8802,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9950,7 +8818,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9967,7 +8834,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9984,7 +8850,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10001,7 +8866,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10018,7 +8882,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10035,7 +8898,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10052,7 +8914,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10069,7 +8930,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10086,7 +8946,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10103,7 +8962,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10120,7 +8978,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10137,7 +8994,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10154,7 +9010,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10171,7 +9026,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10185,7 +9039,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -10229,14 +9083,6 @@ "node": ">=10" } }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/semver-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", @@ -10300,37 +9146,6 @@ "node": ">= 0.8" } }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -10530,143 +9345,11 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -10685,31 +9368,13 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.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==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true - }, "node_modules/stack-trace": { "version": "1.0.0-pre2", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0-pre2.tgz", @@ -10904,32 +9569,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/superjson": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", - "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "copy-anything": "^3.0.2" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10960,7 +9599,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "sync-message-port": "^1.0.0" @@ -10973,7 +9612,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=16.0.0" @@ -10995,7 +9634,7 @@ "version": "5.37.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -11014,7 +9653,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/text-decoder": { @@ -11152,7 +9791,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/type-check": { @@ -11344,32 +9983,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "dev": true, - "license": "MIT" - }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "license": "MIT" }, - "node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", @@ -11527,7 +10146,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/vary": { @@ -11543,7 +10162,6 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -11611,19 +10229,6 @@ } } }, - "node_modules/vite-hot-client": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-0.2.4.tgz", - "integrity": "sha512-a1nzURqO7DDmnXqabFOliz908FRmIppkBKsJthS8rbe8hBEXwEwe4C3Pp33Z1JoFCYfVL4kTOMLKk0ZZxREIeA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" - } - }, "node_modules/vite-plugin-checker": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.8.0.tgz", @@ -12214,13 +10819,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "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==", - "dev": true, - "license": "ISC" - }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -12239,28 +10837,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/xdg-basedir": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", @@ -12283,15 +10859,6 @@ "node": ">=12" } }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -12312,7 +10879,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, + "devOptional": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -12368,27 +10935,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yauzl/node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -12402,19 +10948,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoctocolors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yoctocolors-cjs": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", diff --git a/package.json b/package.json index c3c4c86..f1ef496 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,10 @@ "dependencies": { "@quasar/cli": "^2.5.0", "@quasar/extras": "^1.16.4", + "@quasar/vite-plugin": "^1.9.0", "axios": "^1.2.1", "pinia": "^2.0.11", - "quasar": "^2.16.0", + "quasar": "^2.18.1", "vue": "^3.4.18", "vue-i18n": "^9.2.2", "vue-router": "^4.0.12" @@ -30,7 +31,6 @@ "@twa-dev/types": "^8.0.2", "@types/node": "^20.17.30", "@types/telegram-web-app": "^7.10.1", - "@vue/devtools": "^7.7.2", "@vue/eslint-config-typescript": "^14.1.3", "autoprefixer": "^10.4.2", "eslint": "^9.14.0", diff --git a/quasar.config.ts b/quasar.config.ts index cfae256..ef4bf55 100644 --- a/quasar.config.ts +++ b/quasar.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from '#q-app/wrappers' import { fileURLToPath } from 'node:url' +import path from 'path' export default defineConfig((ctx) => { return { @@ -13,11 +14,11 @@ export default defineConfig((ctx) => { // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli-vite/boot-files boot: [ + 'telegram-boot', 'i18n', 'axios', 'auth-init', - 'global-components', - 'telegram-boot' + 'global-components' ], // https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#css @@ -52,11 +53,14 @@ export default defineConfig((ctx) => { // extendTsConfig (tsConfig) {} }, + alias: { + 'composables': path.resolve(__dirname, './src/composables'), + 'types': path.resolve(__dirname, './src/types') + }, + vueRouterMode: 'history', // available values: 'hash', 'history' - vueDevtools: true, // Должно быть true - devtool: 'source-map', // Для лучшей отладки + // devtool: 'source-map', // Для лучшей отладки // vueRouterBase, - // vueDevtools, // vueOptionsAPI: false, // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup @@ -103,14 +107,13 @@ export default defineConfig((ctx) => { // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#devserver devServer: { - vueDevtools: true, port: 9000, proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true } - } + }, // https: true, // open: true // opens browser window automatically }, @@ -250,4 +253,4 @@ export default defineConfig((ctx) => { extraScripts: [] } } -}); +}) diff --git a/src/App.vue b/src/App.vue index 2df1ee8..a47e780 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,23 +4,34 @@ diff --git a/src/boot/axios.ts b/src/boot/axios.ts index 18256d4..adde349 100644 --- a/src/boot/axios.ts +++ b/src/boot/axios.ts @@ -1,6 +1,5 @@ import { defineBoot } from '#q-app/wrappers' import axios, { type AxiosError } from 'axios' -import { useAuthStore } from 'src/stores/auth' class ServerError extends Error { constructor( @@ -30,10 +29,6 @@ api.interceptors.response.use( errorData.message ) - if (error.response?.status === 401) { - await useAuthStore().logout() - } - return Promise.reject(serverError) } ) @@ -42,4 +37,4 @@ export default defineBoot(({ app }) => { app.config.globalProperties.$api = api }) -export { api, ServerError } \ No newline at end of file +export { api, ServerError } diff --git a/src/boot/helpers.ts b/src/boot/helpers.ts index 6c1fb46..6b1a2ea 100644 --- a/src/boot/helpers.ts +++ b/src/boot/helpers.ts @@ -1,33 +1,73 @@ -export function isObjEqual (obj1: Record, obj2: Record): boolean { - const filteredObj1 = filterIgnored(obj1) - const filteredObj2 = filterIgnored(obj2) +function isDirty ( + obj1: Record | null | undefined, + obj2: Record | null | undefined +): boolean { + const actualObj1 = obj1 ?? {} + const actualObj2 = obj2 ?? {} + + const filteredObj1 = filterIgnored(actualObj1) + const filteredObj2 = filterIgnored(actualObj2) const allKeys = new Set([...Object.keys(filteredObj1), ...Object.keys(filteredObj2)]) for (const key of allKeys) { - const hasKey1 = Object.prototype.hasOwnProperty.call(filteredObj1, key) - const hasKey2 = Object.prototype.hasOwnProperty.call(filteredObj2, key) + const hasKey1 = Object.hasOwn(filteredObj1, key) + const hasKey2 = Object.hasOwn(filteredObj2, key) if (hasKey1 !== hasKey2) return false - if (hasKey1 && hasKey2 && filteredObj1[key] !== filteredObj2[key]) return false + + if (hasKey1 && hasKey2) { + const val1 = filteredObj1[key] + const val2 = filteredObj2[key] + + if (typeof val1 === 'string' && typeof val2 === 'string') { + if (val1.trim() !== val2.trim()) return false + } else if (val1 !== val2) { + return false + } + } } return true } -function filterIgnored(obj: Record): Record { +function filterIgnored(obj: Record): Record { const filtered: Record = {} for (const key in obj) { - const value = obj[key] - if (value !== "" && value !== 0 && value !== false) filtered[key] = value + const originalValue = obj[key] + + // Пропускаем значения, которые не string, number или boolean + if ( + typeof originalValue !== 'string' && + typeof originalValue !== 'number' && + typeof originalValue !== 'boolean' + ) { + continue + } + + let value = originalValue + + if (typeof value === 'string') { + value = value.trim() + if (value === '') continue + } + + if (value === 0 || value === false) continue + + filtered[key] = value } return filtered } -export function parseIntString (s: string | string[] | undefined) :number | null { +function parseIntString (s: string | string[] | undefined) :number | null { if (typeof s !== 'string') return null const regex = /^[+-]?\d+$/ return regex.test(s) ? Number(s) : null +} + +export { + isDirty, + parseIntString } \ No newline at end of file diff --git a/src/boot/i18n.ts b/src/boot/i18n.ts index 5b89b30..f273438 100644 --- a/src/boot/i18n.ts +++ b/src/boot/i18n.ts @@ -1,11 +1,11 @@ -import { defineBoot } from '#q-app/wrappers'; -import { createI18n } from 'vue-i18n'; +import { defineBoot } from '#q-app/wrappers' +import { createI18n } from 'vue-i18n' -import messages from 'src/i18n'; +import messages from 'src/i18n' -export type MessageLanguages = keyof typeof messages; +export type MessageLanguages = keyof typeof messages // Type-define 'en-US' as the master schema for the resource -export type MessageSchema = typeof messages['en-US']; +export type MessageSchema = typeof messages['en-US'] // See https://vue-i18n.intlify.dev/guide/advanced/typescript.html#global-resource-schema-type-definition /* eslint-disable @typescript-eslint/no-empty-object-type */ @@ -26,8 +26,8 @@ export default defineBoot(({ app }) => { locale: 'en-US', legacy: false, messages, - }); + }) // Set i18n instance on app - app.use(i18n); -}); + app.use(i18n) +}) diff --git a/src/components/pnAccountBlockName.vue b/src/components/pnAccountBlockName.vue index 409437e..a4548ad 100644 --- a/src/components/pnAccountBlockName.vue +++ b/src/components/pnAccountBlockName.vue @@ -13,7 +13,7 @@ tgUser?.first_name + (tgUser?.first_name && tgUser?.last_name ? ' ' : '') + tgUser?.last_name + - !(tgUser?.first_name || tgUser?.last_name) ? tgUser?.username : '' + (!(tgUser?.first_name || tgUser?.last_name) ? tgUser?.username : '') }} diff --git a/src/components/projectInfoBlock.vue b/src/components/projectInfoBlock.vue index 15cb87e..5b2d018 100644 --- a/src/components/projectInfoBlock.vue +++ b/src/components/projectInfoBlock.vue @@ -12,23 +12,27 @@ no-error-icon dense filled + label-slot class = "w100 fix-bottom-padding" - :label="$t('project_card__project_name')" :rules="[rules.name]" - /> + > + + @@ -41,7 +45,7 @@ diff --git a/src/composables/useNotify.ts b/src/composables/useNotify.ts new file mode 100644 index 0000000..992de71 --- /dev/null +++ b/src/composables/useNotify.ts @@ -0,0 +1,22 @@ +import { useQuasar } from 'quasar' +import { useI18n } from 'vue-i18n' +import type { ServerError } from 'boot/axios' + +export type { ServerError } + +export function useNotify() { + const $q = useQuasar() + const { t } = useI18n() + + const notifyError = (error: ServerError) => { + $q.notify({ + message: `${t(error.message)} (${t('code')}: ${error.code})`, + type: 'negative', + position: 'bottom', + timeout: 2000, + multiLine: true + }) + } + + return { notifyError} +} diff --git a/src/i18n/en-US/index.ts b/src/i18n/en-US/index.ts index 2047dca..4f88d77 100644 --- a/src/i18n/en-US/index.ts +++ b/src/i18n/en-US/index.ts @@ -1 +1 @@ -export default { EN: 'EN', RU: 'RU', continue: 'Continue', back: 'Back', month: 'month', months: 'months', slogan: 'Work together - it\'s magic!', under_construction: 'Under construction.', login__email: 'E-mail', login__password: 'Password', login__forgot_password: 'Forgot Password?', login__sign_in: 'Log in', login__incorrect_login_data: 'User data not found. Edit your auth details before continuing', login__or_continue_as: 'or continue as', login__terms_of_use: 'Terms of use', login__accept_terms_of_use: 'I accept the', login__register: 'Create account', login__registration_message_error: 'Error', login__licensing_agreement: 'Licensing agreement', login__have_account: 'Already have an accont?', login__incorrect_email: 'Please enter a valid email address', login__password_require: 'At least 8 characters', user__logout: 'Logout', projects__projects: 'Projects', projects__show_archive: 'Show archive', projects__hide_archive: 'Hide archive', projects__restore_archive_warning: 'Attention!', projects__restore_archive_warning_message: 'To restore a project from an archive, you must manually attach chats to it.', projects__lets_start: 'Create a Project', projects__lets_start_description: 'Projects isolate data: contacts, tasks, documents, and chats are visible only to members', project__chats: 'Chats', project__persons: 'Persons', project__companies: 'Companies', project__edit: 'Edit', project__backup: 'Backup', project__archive: 'Archive', project__archive_warning: 'Are you sure?', project__archive_warning_message: 'Chat tracking in the project will be disabled after moving to the archive.', project__delete: 'Delete', project__delete_warning: 'Warning!', project__delete_warning_message: 'All project data will be removed. This action cannot be undone.', project_chats__search: 'Search', project_chats__send_chat: 'Request for attach chat', project_chats__send_chat_description: 'Provide instructions to the chat admin', project_chats__attach_chat: 'Attach chat', project_chats__attach_chat_description: 'Requires chat administrator privileges', project_chat__delete_warning: 'Warning!', project_chat__delete_warning_message: 'Chat tracking will be discontinued. If necessary, the cat can be attached again.', project_card__project_card: 'Project card', project_card__add_project: 'Add project', project_card__project_name: 'Name', project_card__project_description: 'Description', project_card__btn_accept: 'Accept', project_card__btn_back: 'Back', project_card__image_use_as_background_chats: 'logo as background for chats', project_card__error_name: 'Field is required', forgot_password__password_recovery: 'Password recovery', account_helper__enter_email: 'Enter account e-mail', account_helper__email: 'E-mail', account_helper__confirm_email: 'Confirm e-mail', account_helper__confirm_email_message: 'Enter the Code from e-mail to continue recover your password. If you haven\'t received an e-mail with the Code, check the Spam folder.', account_helper__code: 'Code', account_helper__code_error: 'Incorrect code. Ensure your e-mail is correct and try again.', account_helper__set_password: 'Set password', account_helper__password: 'Password', account_helper__finish: 'Finish', account_helper__finish_after_message: 'Done!', account__user_settings: 'User settings', account__your_company: 'Your company', account__change_auth_message_2: 'After creating a user, all data from the Telegram account will be transferred to the new account.', account__change_auth_btn: 'Create system account', account__change_auth_warning: 'WARNING!', account__change_auth_warning_message: 'Reverse data transfer is not possible.', account__chats: 'Chats', account__chats_active: 'Active', account__chats_unbound: 'Unbound', account__chats_free: 'Free', account__chats_total: 'Total', account__subscribe: 'Subscribe', account__subscribe_description: 'With a subscription, you can attach more active chats.', account__auth_change_method: 'Change authorization method', account__auth_change_method_description: 'In case of corporate use, it is recommended to log in with a username and password.', account__auth_change_password: 'Change account password', account__auth_change_password_description: 'Access to the email address used for system login is required.', account__auth_change_account: 'Change account e-mail', account__auth_change_account_description: 'Access to both the current and new email addresses used for system authentication is required.', account__company_data: 'Your company data', account__company_data_description: 'Projects will automatically include this data.', account__manual: 'Manual', account__manual_description: 'Go to our Telegram channel with video tutorials.', account__support: 'Support', account__support_description: 'Need help? Contact us!', account__terms_of_use: 'Terms of use', account__privacy: 'Privacy and Cookie Policy', company__mask: 'Company cloacking', mask__title_table: 'Excluded', mask__help_title: 'Cloacking', mask__help_message: 'It is possible to cloacking a company by representing its personnel as your own to companies other than those on the exclusion list.', company_info__title_card: 'Company card', company_info__name: 'Name', company_info__description: 'Description', company_info__persons: 'Persons', company_create__title_card: 'Add company', project_persons__search: 'Search', person_card__title: 'Person card', person_card__name: 'Name', person_card__company: 'Company name', person_card__department: 'Department', person_card__role: 'Role', settings__title: 'Settings', settings__language: 'Language', settings__font_size: 'Font size', terms__title: 'Terms of use', subscribe__title: 'Subscribe', subscribe__current_balance: 'Current balance', subscribe__token_formula: '1 = 1 day of access to 1 chat', subscribe__token_formula_description: 'unbound and free chats are not counted', subscribe__info: 'With a subscription, you can attach more chats. Archived chats are not counted.', subscribe__about: 'about', subscribe__select_payment_1: 'You can pay for your subscription using ', subscribe__select_payment_2: 'Telegram stars', subscribe__select_option_1: 'Telegram stars', subscribe__select_option_2: 'Telegram stars', subscribe__select_option_3: 'Telegram stars', subscribe__select_option_user: 'Telegram stars' } \ No newline at end of file +export default { EN: 'EN', RU: 'RU', continue: 'Continue', back: 'Back', code: 'Code', month: 'month', months: 'months', slogan: 'Work together - it\'s magic!', under_construction: 'Under construction.', login__email: 'E-mail', login__password: 'Password', login__forgot_password: 'Forgot Password?', login__sign_in: 'Log in', login__incorrect_login_data: 'User data not found. Edit your auth details before continuing', login__or_continue_as: 'or continue as', login__terms_of_use: 'Terms of use', login__accept_terms_of_use: 'I accept the', login__register: 'Create account', login__registration_message_error: 'Error', login__licensing_agreement: 'Licensing agreement', login__have_account: 'Already have an accont?', login__incorrect_email: 'Please enter a valid email address', login__password_require: 'At least 8 characters', user__logout: 'Logout', projects__projects: 'Projects', projects__show_archive: 'Show archive', projects__hide_archive: 'Hide archive', projects__restore_archive_warning: 'Attention!', projects__restore_archive_warning_message: 'To restore a project from an archive, you must manually attach chats to it.', projects__lets_start: 'Create a Project', projects__lets_start_description: 'Projects isolate data: contacts, tasks, documents, and chats are visible only to members', project__chats: 'Chats', project__persons: 'Persons', project__companies: 'Companies', project__edit: 'Edit', project__backup: 'Backup', project__archive: 'Archive', project__archive_warning: 'Are you sure?', project__archive_warning_message: 'Chat tracking in the project will be disabled after moving to the archive.', project__delete: 'Delete', project__delete_warning: 'Warning!', project__delete_warning_message: 'All project data will be removed. This action cannot be undone.', project_chats__search: 'Search', project_chats__send_chat: 'Request for attach chat', project_chats__send_chat_description: 'Provide instructions to the chat admin', project_chats__attach_chat: 'Attach chat', project_chats__attach_chat_description: 'Requires chat administrator privileges', project_chat__delete_warning: 'Warning!', project_chat__delete_warning_message: 'Chat tracking will be discontinued. If necessary, the cat can be attached again.', project_card__project_card: 'Project card', project_card__add_project: 'Add project', project_card__project_name: 'Name', project_card__project_description: 'Description', project_card__btn_accept: 'Accept', project_card__btn_back: 'Back', project_card__image_use_as_background_chats: 'logo as background for chats', project_card__error_name: 'Field is required', forgot_password__password_recovery: 'Password recovery', account_helper__enter_email: 'Enter account e-mail', account_helper__email: 'E-mail', account_helper__confirm_email: 'Confirm e-mail', account_helper__confirm_email_message: 'Enter the Code from e-mail to continue recover your password. If you haven\'t received an e-mail with the Code, check the Spam folder.', account_helper__code: 'Code', account_helper__code_error: 'Incorrect code. Ensure your e-mail is correct and try again.', account_helper__set_password: 'Set password', account_helper__password: 'Password', account_helper__finish: 'Finish', account_helper__finish_after_message: 'Done!', account__user_settings: 'User settings', account__your_company: 'Your company', account__change_auth_message_2: 'After creating a user, all data from the Telegram account will be transferred to the new account.', account__change_auth_btn: 'Create system account', account__change_auth_warning: 'WARNING!', account__change_auth_warning_message: 'Reverse data transfer is not possible.', account__chats: 'Chats', account__chats_active: 'Active', account__chats_unbound: 'Unbound', account__chats_free: 'Free', account__chats_total: 'Total', account__subscribe: 'Subscribe', account__subscribe_description: 'With a subscription, you can attach more active chats.', account__auth_change_method: 'Change authorization method', account__auth_change_method_description: 'In case of corporate use, it is recommended to log in with a username and password.', account__auth_change_password: 'Change account password', account__auth_change_password_description: 'Access to the email address used for system login is required.', account__auth_change_account: 'Change account e-mail', account__auth_change_account_description: 'Access to both the current and new email addresses used for system authentication is required.', account__company_data: 'Your company data', account__company_data_description: 'Projects will automatically include this data.', account__manual: 'Manual', account__manual_description: 'Go to our Telegram channel with video tutorials.', account__settings: 'Settings', account__settings_description: 'Fonts size, language and etc.', account__support: 'Support', account__support_description: 'Need help? Contact us!', account__terms_of_use: 'Terms of use', account__privacy: 'Privacy and Cookie Policy', company__mask: 'Company cloacking', mask__title_table: 'Excluded', mask__help_title: 'Cloacking', mask__help_message: 'It is possible to cloacking a company by representing its personnel as your own to companies other than those on the exclusion list.', company_info__title_card: 'Company card', company_info__name: 'Name', company_info__description: 'Description', company_info__persons: 'Persons', company_create__title_card: 'Add company', project_persons__search: 'Search', person_card__title: 'Person card', person_card__name: 'Name', person_card__company: 'Company name', person_card__department: 'Department', person_card__role: 'Role', settings__title: 'Settings', settings__language: 'Language', settings__font_size: 'Font size', terms__title: 'Terms of use', subscribe__title: 'Subscribe', subscribe__current_balance: 'Current balance', subscribe__token_formula: '1 = 1 day of access to 1 chat', subscribe__token_formula_description: 'unbound and free chats are not counted', subscribe__info: 'With a subscription, you can attach more chats. Archived chats are not counted.', subscribe__about: 'about', subscribe__select_payment_1: 'You can pay for your subscription using ', subscribe__select_payment_2: 'Telegram stars', subscribe__select_option_1: 'Telegram stars', subscribe__select_option_2: 'Telegram stars', subscribe__select_option_3: 'Telegram stars', subscribe__select_option_user: 'Telegram stars', AUTH_ERROR: 'Incorrect e-mail or password!' } \ No newline at end of file diff --git a/src/i18n/ru-RU/index.ts b/src/i18n/ru-RU/index.ts index f88fd31..1a5e237 100644 --- a/src/i18n/ru-RU/index.ts +++ b/src/i18n/ru-RU/index.ts @@ -1 +1 @@ -export default { EN: 'EN', RU: 'RU', continue: 'Продолжить', back: 'Назад', month: 'мес.', months: 'мес.', slogan: 'Работайте вместе - это волшебство!', under_construction: 'В разработке.', login__email: 'Электронная почта', login__password: 'Пароль', login__forgot_password: 'Забыли пароль?', login__sign_in: 'Войти', login__incorrect_login_data: 'Пользователь с такими данными не найден. Отредактируйте введенные данные', login__or_continue_as: 'или продолжить', login__terms_of_use: 'Пользовательское соглашение', login__accept_terms_of_use: 'Я принимаю', login__register: 'Зарегестрироваться', login__registration_message_error: 'Ошибка', login__licensing_agreement: 'Договор о лицензировании', login__have_account: 'Есть учетная запись', login__incorrect_email: 'Адрес почты некорректный', login__password_require: 'Мин. 8 символов', user__logout: 'Выход', projects__projects: 'Проекты', projects__show_archive: 'Показать архив', projects__hide_archive: 'Скрыть архив', projects__restore_archive_warning: 'Внимание!', projects__restore_archive_warning_message: 'При восстановлении проекта из архива - присоединение чатов к проекту требуется осуществлять вручную.', projects__lets_start: 'Создайте проект', projects__lets_start_description: 'Проекты помогают изолировать данные: контакты, задачи, документы и чаты доступны только участникам', project__chats: 'Чаты', project__persons: 'Люди', project__companies: 'Компании', project__edit: 'Редактировать', project__backup: 'Резервная копия', project__archive: 'В архив', project__archive_warning: 'Вы уверены?', project__archive_warning_message: 'После перемещения проекта в архив отслеживание чатов будет отключено.', project__delete: 'Удалить', project__delete_warning: 'Внимание!', project__delete_warning_message: 'Все данные проекта будут безвозвратно удалены.', project_chats__search: 'Поиск', project_chats__send_chat: 'Запрос на добавление чата', project_chats__send_chat_description: 'Отправить инструкцию администратору чата', project_chats__attach_chat: 'Добавить чат', project_chats__attach_chat_description: 'Необходимы права администратора чата', project_chat__delete_warning: 'Внимание!', project_chat__delete_warning_message: 'Отслеживание чата будет прекращено. При необходимости чат можно будет подключить снова.', project_card__project_card: 'Карточка компании', project_card__add_project: 'Новый проект', project_card__project_name: 'Название', project_card__project_description: 'Описание', project_card__btn_accept: 'Подтвердить', project_card__btn_back: 'Назад', project_card__image_use_as_background_chats: 'логотип в качестве фона для чатов', project_card__error_name: 'Поле обязательно к заполнению', forgot_password__password_recovery: 'Восстановление пароля', account_helper__enter_email: 'Введите электронную почту', account_helper__email: 'Электронная почта', account_helper__confirm_email: 'Подтверждение электронной почты', account_helper__confirm_email_message: 'Введите код из письма для продолжения восстановления пароля. Если не получили письмо с кодом - проверьте папку Спам', account_helper__code: 'Код', account_helper__code_error: 'Был введен неверный код. Проверьте адрес электронной почты и повторите попытку.', account_helper__set_password: 'Установка пароля', account_helper__password: 'Пароль', account_helper__finish: 'Отправить', account_helper__finish_after_message: 'Готово!', account__user_settings: 'Пользовательские настройки', account__your_company: 'Ваша компания', account__change_auth_message_2: 'После создания пользователя все данные с учетной записи Telegram будут перенесены на новую учетную запись.', account__change_auth_btn: 'Создать пользователя', account__change_auth_warning: 'ВНИМАНИЕ!', account__change_auth_warning_message: 'Обратный перенос данных не возможен.', account__chats: 'Чаты', account__chats_active: 'Активные', account__chats_unbound: 'Открепленные', account__chats_free: 'Бесплатные', account__chats_total: 'Всего', account__subscribe: 'Подписка', account__subscribe_description: 'С помощью подписки можно подключить дополнительные чаты.', account__auth_change_method: 'Сменить способ авторизации', account__auth_change_method_description: 'В случае корпоративного использования рекомендуется входить в систему, указав логин и пароль.', account__auth_change_password: 'Изменить пользовательский пароль', account__auth_change_password_description: 'Необходим доступ к электронной почте, используемой для входа в систему.', account__auth_change_account: 'Сменить электронную почту учетной записи', account__auth_change_account_description: 'Необходим доступ к текущей и новой электронной почте, используемым для входа в систему.', account__company_data: 'Данные вашей компании', account__company_data_description: 'Эти данные будут автоматически подгружаться в проекты. ', account__manual: 'Инструкции', account__manual_description: 'Перейдите в наш Telegram-канал с обучающими видеороликами.', account__support: 'Поддержка', account__support_description: 'Есть вопросы - напишите нам!', account__terms_of_use: 'Пользовательское соглашение', account__privacy: 'Политика конфидециальности', company__mask: 'Маскировка компаний', mask__title_table: 'Исключения', mask__help_title: 'Маскировка', mask__help_message: 'Возможно замаскировать компанию, представляя ее персонал как собственный для других компаний, кроме тех, что есть в перечне исключений. ', company_info__title_card: 'Карточка компании', company_info__name: 'Название', company_info__description: 'Описание', company_info__persons: 'Сотрудники', company_create__title_card: 'Добавление компании', project_persons__search: 'Поиск', person_card__title: 'Карточка сотрудника', person_card__name: 'ФИО', person_card__company: 'Название компании', person_card__department: 'Подразделение', person_card__role: 'Функционал (должность)', settings__title: 'Настройки', settings__language: 'Язык', settings__font_size: 'Размер шрифта', terms__title: 'Пользовательское соглашение', subscribe__title: 'Подписка', subscribe__current_balance: 'Текущий баланс', subscribe__token_formula: '1 = 1 день подключения к 1 чату', subscribe__token_formula_description: 'отвязанные и бесплатные чаты не учитываются', subscribe__info: 'С помощью подписки можно подключить к бесплатным групповым чатам дополнительные. Архивные чаты не учитываются. ', subscribe__about: 'около', subscribe__select_payment_1: 'Вы можете оплатить подписку с помощью', subscribe__select_payment_2: 'Telegram stars', subscribe__select_option_1: 'Telegram stars', subscribe__select_option_2: 'Telegram stars', subscribe__select_option_3: 'Telegram stars', subscribe__select_option_user: 'Telegram stars' } \ No newline at end of file +export default { EN: 'EN', RU: 'RU', continue: 'Продолжить', back: 'Назад', code: 'Код', month: 'мес.', months: 'мес.', slogan: 'Работайте вместе - это волшебство!', under_construction: 'В разработке.', login__email: 'Электронная почта', login__password: 'Пароль', login__forgot_password: 'Забыли пароль?', login__sign_in: 'Войти', login__incorrect_login_data: 'Пользователь с такими данными не найден. Отредактируйте введенные данные', login__or_continue_as: 'или продолжить', login__terms_of_use: 'Пользовательское соглашение', login__accept_terms_of_use: 'Я принимаю', login__register: 'Зарегистрироваться', login__registration_message_error: 'Ошибка', login__licensing_agreement: 'Договор о лицензировании', login__have_account: 'Есть учетная запись', login__incorrect_email: 'Адрес почты некорректный', login__password_require: 'Мин. 8 символов', user__logout: 'Выход', projects__projects: 'Проекты', projects__show_archive: 'Показать архив', projects__hide_archive: 'Скрыть архив', projects__restore_archive_warning: 'Внимание!', projects__restore_archive_warning_message: 'При восстановлении проекта из архива - присоединение чатов к проекту требуется осуществлять вручную.', projects__lets_start: 'Создайте проект', projects__lets_start_description: 'Проекты помогают изолировать данные: контакты, задачи, документы и чаты доступны только участникам', project__chats: 'Чаты', project__persons: 'Люди', project__companies: 'Компании', project__edit: 'Редактировать', project__backup: 'Резервная копия', project__archive: 'В архив', project__archive_warning: 'Вы уверены?', project__archive_warning_message: 'После перемещения проекта в архив отслеживание чатов будет отключено.', project__delete: 'Удалить', project__delete_warning: 'Внимание!', project__delete_warning_message: 'Все данные проекта будут безвозвратно удалены.', project_chats__search: 'Поиск', project_chats__send_chat: 'Запрос на добавление чата', project_chats__send_chat_description: 'Отправить инструкцию администратору чата', project_chats__attach_chat: 'Добавить чат', project_chats__attach_chat_description: 'Необходимы права администратора чата', project_chat__delete_warning: 'Внимание!', project_chat__delete_warning_message: 'Отслеживание чата будет прекращено. При необходимости чат можно будет подключить снова.', project_card__project_card: 'Карточка проекта', project_card__add_project: 'Новый проект', project_card__project_name: 'Название', project_card__project_description: 'Описание', project_card__btn_accept: 'Подтвердить', project_card__btn_back: 'Назад', project_card__image_use_as_background_chats: 'логотип в качестве фона для чатов', project_card__error_name: 'Поле обязательно к заполнению', forgot_password__password_recovery: 'Восстановление пароля', account_helper__enter_email: 'Введите электронную почту', account_helper__email: 'Электронная почта', account_helper__confirm_email: 'Подтверждение электронной почты', account_helper__confirm_email_message: 'Введите код из письма для продолжения восстановления пароля. Если не получили письмо с кодом - проверьте папку Спам', account_helper__code: 'Код', account_helper__code_error: 'Был введен неверный код. Проверьте адрес электронной почты и повторите попытку.', account_helper__set_password: 'Установка пароля', account_helper__password: 'Пароль', account_helper__finish: 'Отправить', account_helper__finish_after_message: 'Готово!', account__user_settings: 'Пользовательские настройки', account__your_company: 'Ваша компания', account__change_auth_message_2: 'После создания пользователя все данные с учетной записи Telegram будут перенесены на новую учетную запись.', account__change_auth_btn: 'Создать пользователя', account__change_auth_warning: 'ВНИМАНИЕ!', account__change_auth_warning_message: 'Обратный перенос данных не возможен.', account__chats: 'Чаты', account__chats_active: 'Активные', account__chats_unbound: 'Открепленные', account__chats_free: 'Бесплатные', account__chats_total: 'Всего', account__subscribe: 'Подписка', account__subscribe_description: 'С помощью подписки можно подключить дополнительные чаты.', account__auth_change_method: 'Сменить способ авторизации', account__auth_change_method_description: 'В случае корпоративного использования рекомендуется входить в систему, указав логин и пароль.', account__auth_change_password: 'Изменить пользовательский пароль', account__auth_change_password_description: 'Необходим доступ к электронной почте, используемой для входа в систему.', account__auth_change_account: 'Сменить электронную почту учетной записи', account__auth_change_account_description: 'Необходим доступ к текущей и новой электронной почте, используемым для входа в систему.', account__company_data: 'Данные вашей компании', account__company_data_description: 'Эти данные будут автоматически подгружаться в проекты. ', account__manual: 'Инструкции', account__manual_description: 'Перейдите в наш Telegram-канал с обучающими видеороликами.', account__settings: 'Настройки', account__settings_description: 'Размер шрифта, язык и т.п.', account__support: 'Поддержка', account__support_description: 'Есть вопросы - напишите нам!', account__terms_of_use: 'Пользовательское соглашение', account__privacy: 'Политика конфидециальности', company__mask: 'Маскировка компаний', mask__title_table: 'Исключения', mask__help_title: 'Маскировка', mask__help_message: 'Возможно замаскировать компанию, представляя ее персонал как собственный для других компаний, кроме тех, что есть в перечне исключений. ', company_info__title_card: 'Карточка компании', company_info__name: 'Название', company_info__description: 'Описание', company_info__persons: 'Сотрудники', company_create__title_card: 'Добавление компании', project_persons__search: 'Поиск', person_card__title: 'Карточка сотрудника', person_card__name: 'ФИО', person_card__company: 'Название компании', person_card__department: 'Подразделение', person_card__role: 'Функционал (должность)', settings__title: 'Настройки', settings__language: 'Язык', settings__font_size: 'Размер шрифта', terms__title: 'Пользовательское соглашение', subscribe__title: 'Подписка', subscribe__current_balance: 'Текущий баланс', subscribe__token_formula: '1 = 1 день подключения к 1 чату', subscribe__token_formula_description: 'отвязанные и бесплатные чаты не учитываются', subscribe__info: 'С помощью подписки можно подключить к бесплатным групповым чатам дополнительные. Архивные чаты не учитываются. ', subscribe__about: 'около', subscribe__select_payment_1: 'Вы можете оплатить подписку с помощью', subscribe__select_payment_2: 'Telegram stars', subscribe__select_option_1: 'Telegram stars', subscribe__select_option_2: 'Telegram stars', subscribe__select_option_3: 'Telegram stars', subscribe__select_option_user: 'Telegram stars', AUTH_ERROR: 'Электронная почта или пароль введены неверно!' } \ No newline at end of file diff --git a/src/pages/AccountPage.vue b/src/pages/AccountPage.vue index 8825892..483b43b 100644 --- a/src/pages/AccountPage.vue +++ b/src/pages/AccountPage.vue @@ -4,7 +4,6 @@
([ { id: 1, name: 'account__subscribe', description: 'account__subscribe_description', icon: 'mdi-crown-circle-outline', iconColor: 'orange', pathName: 'subscribe' }, @@ -73,6 +71,7 @@ async function logout () { await authStore.logout() + await router.push({ name: 'login' }) } diff --git a/src/pages/LoginPage.vue b/src/pages/LoginPage.vue index 8cb22ee..3e30387 100644 --- a/src/pages/LoginPage.vue +++ b/src/pages/LoginPage.vue @@ -1,10 +1,11 @@