before delete 3software

This commit is contained in:
2025-06-29 18:55:59 +03:00
parent ebd77a3e66
commit b51a472738
147 changed files with 257326 additions and 3151 deletions

View File

@@ -1,15 +1,27 @@
const crypto = require('crypto')
const express = require('express')
const WebSocket = require('ws')
const db = require('../include/db')
const log = require('../include/log')
const eventBus = require('../include/eventbus')
const bot = require('./bot')
const fs = require('fs')
const app = express.Router()
const sessions = {}
function registerWS(sid, ws) {
const session = sessions[sid]
if (session)
session.ws = ws
return !!session
}
const cache = {
// email -> code
register: {},
upgrade: {},
recovery: {},
'change-password': {},
'change-email': {},
@@ -42,8 +54,8 @@ app.use((req, res, next) => {
if (public.includes(req.path))
return next()
const asid = req.query.asid || req.cookies.asid
req.session = sessions[asid]
const sid = req.query.sid || req.cookies.sid
req.session = sessions[sid]
if (!req.session)
throw Error('ACCESS_DENIED::401')
@@ -57,9 +69,9 @@ function createSession(req, res, customer_id) {
throw Error('AUTH_ERROR::500')
res.locals.customer_id = customer_id
const asid = crypto.randomBytes(64).toString('hex')
req.session = sessions[asid] = {asid, customer_id }
res.setHeader('Set-Cookie', [`asid=${asid};httpOnly;path=/api/admin`])
const sid = crypto.randomBytes(64).toString('hex')
req.session = sessions[sid] = {sid, customer_id }
res.setHeader('Set-Cookie', [`sid=${sid};httpOnly;path=/api/admin`])
}
app.post('/auth/email', (req, res, next) => {
@@ -67,7 +79,7 @@ app.post('/auth/email', (req, res, next) => {
res.locals.password = req.body?.password
const customer_id = db
.prepare(`select id from customers where is_blocked = 0 and email = :email and password is not null and password = :password `)
.prepare(`select id from customers where email = :email and password is not null and password = :password `)
.pluck(true)
.get(res.locals)
@@ -75,7 +87,7 @@ app.post('/auth/email', (req, res, next) => {
throw Error('AUTH_ERROR::401')
createSession(req, res, customer_id)
res.status(200).json({success: true})
res.status(200).json({success: true })
})
app.post('/auth/telegram', (req, res, next) => {
@@ -83,24 +95,25 @@ app.post('/auth/telegram', (req, res, next) => {
.prepare(`select id from customers where telegram_id = :telegram_id`)
.pluck(true)
.get(res.locals) || db
.prepare(`replace into customers (telegram_id) values (:telegram_id) returning id`)
.prepare(`insert into customers (telegram_id) values (:telegram_id) returning id`)
.pluck(true)
.get(res.locals)
createSession(req, res, customer_id)
res.status(200).json({success: true})
res.status(200).json({ success: true })
})
/*
Регистрация нового клиента выполняется за ТРИ последовательных вызова
Регистрация нового клиента/Перевод авторизации с TG на email выполняется за ТРИ последовательных вызова
1. Отравляется email. Если email корректный и уже неиспользуется, то сервер возвращает ОК и на указанный email отправляется код.
2. Отправляется email + код из письма. Если указан корректный код, то сервер отвечает ОК.
3. Отправляется email + код из письма + желаемый пароль. Если все ОК, то сервер создает учетную запись и возвращает ОК.
*/
app.post('/auth/email/register', (req, res, next) => {
app.post('/auth/email/:action(register|upgrade)', (req, res, next) => {
const email = String(req.body.email ?? '').trim()
const code = String(req.body.code ?? '').trim()
const password = String(req.body.password ?? '').trim()
const action = req.params.action
const stepNo = email && !code ? 1 : email && code && !password ? 2 : email && code && password ? 3 : -1
if (stepNo == -1)
@@ -119,12 +132,12 @@ app.post('/auth/email/register', (req, res, next) => {
throw Error('USED_EMAIL::400')
const code = Math.random().toString().substr(2, 4)
cache.register[email] = code
sendEmail(email, 'REGISTER', `${email} => ${code}`)
cache[action][email] = code
sendEmail(email, action.toUpperCase(), `${email} => ${code}`)
}
if (stepNo == 2) {
if (cache.register[email] != code)
if (cache[action][email] != code)
throw Error('INCORRECT_CODE::400')
}
@@ -132,11 +145,11 @@ app.post('/auth/email/register', (req, res, next) => {
if (!checkPassword(password))
throw Error('INCORRECT_PASSWORD::400')
db
.prepare('insert into customers (email, password) values (:email, :password)')
.run({email, password})
const query = action == 'register' ? 'insert into customers (email, password) values (:email, :password)' :
'update customers set email = :email, password = :password, telegram_id = null where id = :id'
db.prepare(query).run({email, password, id: res.locals.customer_id})
delete cache.register[email]
delete cache[action][email]
}
res.status(200).json({success: true})
@@ -144,23 +157,25 @@ app.post('/auth/email/register', (req, res, next) => {
/*
Смена email выполняется за ЧЕТЫРЕ последовательных вызовов
Смена email выполняется за ПЯТЬ последовательных вызовов
1. Отравляется пустой закпрос. Сервер на email пользователя из базы отправляет код.
2. Отправляется код из письма. Если указан корректный код, то сервер отвечает ОК.
3. Отправляется код из письма + новый email. Сервер отправляет код2 на новый email.
4. Отправлются оба кода и новый email. Если они проходят проверку, то сервер меняет email пользователя на новый и возвращает ОК.
4. Отправлются оба кода и новый email. Если коды проходят проверку, то сервер отвечает ОК.
5. Отправлются оба кода, новые email и password. Если они проходят проверку, то сервер меняет 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 password = String(req.body.password ?? '').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
const stepNo = !code ? 1 : code && !email2 ? 2 : code && email2 && !code2 ? 3 : code && email2 && code2 && !password ? 4 : code && email2 && code2 && password ? 5 : -1
if (stepNo == -1)
throw Error('BAD_STEP::400')
@@ -187,9 +202,17 @@ app.post('/auth/email/change-email', (req, res, next) => {
if (stepNo == 4) {
if (cache['change-email'][email] != code || cache['change-email2'][email2] != code2)
throw Error('INCORRECT_CODE::400')
}
if (stepNo == 5) {
if (!checkPassword(password))
throw Error('INCORRECT_PASSWORD::400')
res.locals.email = email2
res.locals.password = password
const info = db
.prepare('update customers set email = :email where id = :customer_id')
.prepare('update customers set email = :email, password = :password where id = :customer_id')
.run(res.locals)
if (info.changes == 0)
@@ -210,7 +233,7 @@ app.post('/auth/email/change-email', (req, res, next) => {
*/
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 password = String(req.body.password ?? '').trim()
const action = req.params.action
const email = action == 'change-password' ? db
@@ -260,18 +283,21 @@ app.post('/auth/email/:action(change-password|recovery)', (req, res, next) => {
})
app.get('/auth/logout', (req, res, next) => {
if (req.session?.asid)
delete sessions[req.session.asid]
if (req.session?.sid)
delete sessions[req.session.sid]
res.setHeader('Set-Cookie', [`asid=; expired; httpOnly;path=/api/admin`])
res.setHeader('Set-Cookie', [`sid=; expired; httpOnly;path=/api/admin`])
res.status(200).json({success: true})
})
// CUSTOMER
app.get('/customer/profile', (req, res, next) => {
res.locals.time = Math.floor(Date.now() / 1000)
const row = db
.prepare(`
select id, name, email, plan, coalesce(json_balance, '{}') json_balance, coalesce(json_company, '{}') json_company, upload_chat_id
select id, name, email, plan,
coalesce(json_balance, '{}') json_balance, coalesce(json_company, '{}') json_company,
upload_chat_id, generate_key(-id, :time) upload_token
from customers
where id = :customer_id
`)
@@ -298,6 +324,8 @@ app.get('/customer/profile', (req, res, next) => {
app.put('/customer/profile', (req, res, next) => {
if (req.body.company instanceof Object)
req.body.json_company = JSON.stringify(req.body.company)
else
delete req.body?.json_company
const info = db
.prepareUpdate(
@@ -333,96 +361,86 @@ app.put('/customer/settings', (req, res, next) => {
})
// PROJECT
app.get('/project', (req, res, next) => {
const where = req.query.id ? ' and id = ' + parseInt(req.query.id) : ''
const rows = db
function getProject(id, customer_id) {
const row = db
.prepare(`
select id, name, description, logo, is_logo_bg, is_archived,
select id, name, description, logo, is_logo_bg, company_id, 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}
where customer_id = :customer_id and p.id = :id
order by name
`)
.get({id, customer_id})
if (!row)
throw Error('NOT_FOUND::404')
row.is_archived = Boolean(row.is_archived)
row.is_logo_bg = Boolean(row.is_logo_bg)
return row
}
app.get('/project', (req, res, next) => {
const data = db
.prepare(`
select id, name, description, logo, is_logo_bg, company_id, is_archived,
(select count(*) from chats where project_id = p.id) chat_count,
(select count(distinct user_id) from chat_users where chat_id in (select id from chats where project_id = p.id)) user_count
from projects p
where customer_id = :customer_id
order by name
`)
.all(res.locals)
if (where && rows.length == 0)
throw Error('NOT_FOUND::404')
data.forEach(row => {
row.is_archived = Boolean(row.is_archived)
row.is_logo_bg = Boolean(row.is_logo_bg)
})
res.status(200).json({success: true, data: where ? rows[0] : rows})
})
app.get('/project/:pid(\\d+)', (req, res, next) => {
res.redirect(req.baseUrl + `/project?id=${req.params.pid}`)
res.status(200).json({success: true, data})
})
app.post('/project', (req, res, next) => {
res.locals.name = req.body?.name
res.locals.description = req.body?.description
res.locals.logo = req.body?.logo
res.locals.is_logo_bg = 'is_logo_bg' in req.body ? +req.body.is_logo_bg : undefined
const id = db
res.locals.project_id = db
.prepare(`
insert into projects (customer_id, name, description, logo)
values (:customer_id, :name, :description, :logo)
insert into projects (customer_id, name, description, logo, is_logo_bg)
values (:customer_id, :name, :description, :logo, :is_logo_bg)
returning id
`)
.pluck(true)
.get(res.locals)
res.redirect(req.baseUrl + `/project?id=${id}`)
})
app.put('/project/:pid(\\d+)', (req, res, next) => {
res.locals.id = req.params.pid
res.locals.name = req.body?.name
res.locals.description = req.body?.description
res.locals.logo = req.body?.logo
res.locals.is_logo_bg = req.body?.is_logo_bg
const info = db
.prepareUpdate(
'projects',
['name', 'description', 'logo', 'is_logo_bg'],
res.locals,
['id', 'customer_id'])
.run(res.locals)
if (info.changes == 0)
throw Error('NOT_FOUND::404')
res.redirect(req.baseUrl + `/project?id=${req.params.pid}`)
})
app.put('/project/:pid(\\d+)/:action(archive|restore)', async (req, res, next) => {
res.locals.id = req.params.pid
res.locals.is_archived = +(req.params.action == 'archive')
const info = db
.prepare(`
update projects
set is_archived = :is_archived
where id = :id and customer_id = :customer_id and coalesce(is_archived, 0) = not :is_archived
`)
.run(res.locals)
if (info.changes == 0)
throw Error('BAD_REQUEST::400')
const chatIds = db
.prepare(`select id from chats where project_id = :id`)
const json_company = db
.prepare(`select coalesce(json_company, '{}') from customers where id = :customer_id`)
.pluck(true)
.all(res.locals)
.get(res.locals)
for (const chatId of chatIds) {
await bot.sendMessage(chatId, res.locals.is_archived ? 'Проект помещен в архив. Отслеживание сообщений прекращено.' : 'Проект восстановлен из архива.')
}
res.redirect(req.baseUrl + `/project?id=${req.params.pid}`)
res.locals.company_id = addCompany(Object.assign({
name: 'My Company',
address: null,
email: null,
phone: null,
site: null,
description: null,
logo: null
}, JSON.parse(json_company), {project_id: res.locals.project_id}))
db
.prepare(`update projects set company_id = :company_id where id = :project_id`)
.run(res.locals)
const data = getProject(res.locals.project_id, res.locals.customer_id)
res.status(200).json({success: true, data})
})
app.use ('/project/:pid(\\d+)/*', (req, res, next) => {
app.use ('(/project/:pid(\\d+)/*|/project/:pid(\\d+))', (req, res, next) => {
res.locals.project_id = parseInt(req.params.pid)
const row = db
@@ -435,55 +453,138 @@ app.use ('/project/:pid(\\d+)/*', (req, res, next) => {
next()
})
// USER
app.get('/project/:pid(\\d+)/user', (req, res, next) => {
const where = req.query.id ? ' and id = ' + parseInt(req.query.id) : ''
app.get('/project/:pid(\\d+)', (req, res, next) => {
const data = getProject(req.params.pid, res.locals.customer_id)
res.status(200).json({success: true, data})
})
const rows = db
app.put('/project/:pid(\\d+)', (req, res, next) => {
res.locals.id = req.params.pid
res.locals.name = req.body?.name
res.locals.description = req.body?.description
res.locals.logo = req.body?.logo
res.locals.is_logo_bg = 'is_logo_bg' in req.body ? +req.body.is_logo_bg : undefined
const info = db
.prepareUpdate(
'projects',
['name', 'description', 'logo', 'is_logo_bg'],
res.locals,
['id', 'customer_id'])
.run(res.locals)
if (info.changes == 0)
throw Error('NOT_FOUND::404')
const data = getProject(req.params.pid, res.locals.customer_id)
res.status(200).json({success: true, data})
})
app.put('/project/:pid(\\d+)/:action(archive|restore)', async (req, res, next) => {
try {
res.locals.id = req.params.pid
res.locals.is_archived = +(req.params.action == 'archive')
const info = db
.prepare(`
update projects
set is_archived = :is_archived
where id = :id and customer_id = :customer_id and coalesce(is_archived, 0) = not :is_archived
`)
.run(res.locals)
if (info.changes == 0)
throw Error('BAD_REQUEST::400')
const chat_ids = db
.prepare(`select id from chats where project_id = :id`)
.pluck(true)
.all(res.locals)
for (const chat_id of chat_ids) {
await bot.sendMessage(chat_id, res.locals.is_archived ? 'ON_PROJECT_ARCHIVE' : 'ON_PROJECT_RESTORE')
}
const data = getProject(req.params.pid, res.locals.customer_id)
res.status(200).json({success: true, data})
} catch (err) {
next(err)
}
})
// USER
function getUser(id, project_id) {
const row = db
.prepare(`
select u.id, u.telegram_id, u.firstname, u.lastname, u.username, u.photo,
ud.fullname, ud.role, ud.department, ud.is_blocked
ud.fullname, ud.email, ud.phone, ud.role, ud.department, ud.is_blocked,
(select company_id from company_users where user_id = :id) company_id,
(select json_group_array(chat_id) from chat_users where user_id = :id and chat_id in (select id from chats where project_id = :project_id)) chats
from users u
left join user_details ud on u.id = ud.user_id and ud.project_id = :project_id
where id = :id
`)
.safeIntegers(true)
.get({id, project_id})
if (!row)
throw Error('NOT_FOUND::404')
row.chats = JSON.parse(row.chats || '[]')
row.is_blocked = Boolean(row.is_blocked)
return row
}
app.get('/project/:pid(\\d+)/user', (req, res, next) => {
const data = db
.prepare(`
select u.id, u.telegram_id, u.firstname, u.lastname, u.username, u.photo,
ud.fullname, ud.email, ud.phone, ud.role, ud.department, ud.is_blocked,
(select company_id from company_users where user_id = u.id) company_id
from users u
left join user_details ud on u.id = ud.user_id and ud.project_id = :project_id
where id in (
select user_id
from chat_users
where chat_id in (select id from chats where project_id = :project_id)
) ${where}
)
`)
.safeIntegers(true)
.all(res.locals)
if (where && rows.length == 0)
throw Error('NOT_FOUND::404')
data.forEach(row => {
row.is_blocked = Boolean(row.is_blocked)
})
res.status(200).json({success: true, data: where ? rows[0] : rows})
res.status(200).json({success: true, data})
})
app.get('/project/:pid(\\d+)/user/:uid(\\d+)', (req, res, next) => {
res.redirect(req.baseUrl + `/project/${req.params.pid}/user?id=${req.params.uid}`)
const data = getUser(req.params.uid, req.params.pid)
res.status(200).json({success: true, data})
})
app.put('/project/:pid(\\d+)/user/:uid(\\d+)', (req, res, next) => {
res.locals.user_id = parseInt(req.params.uid)
res.locals.fullname = req.body?.fullname
res.locals.email = req.body?.email
res.locals.phone = req.body?.phone
res.locals.role = req.body?.role
res.locals.department = req.body?.department
res.locals.is_blocked = req.body?.is_blocked
res.locals.is_blocked = 'is_blocked' in req.body ? +req.body.is_blocked : undefined
const info = db
.prepareUpdate('user_details',
['fullname', 'role', 'department', 'is_blocked'],
.prepareUpsert('user_details',
['fullname', 'email', 'phone', 'role', 'department', 'is_blocked'],
res.locals,
['user_id', 'project_id']
)
.all(res.locals)
if (info.changes == 0)
throw Error('NOT_FOUND::404')
.run(res.locals)
res.status(200).json({success: true})
const data = getUser(req.params.uid, req.params.pid)
res.status(200).json({ success: true, data })
})
app.get('/project/:pid(\\d+)/token', (req, res, next) => {
@@ -497,67 +598,92 @@ app.get('/project/:pid(\\d+)/token', (req, res, next) => {
if (!key)
throw Error('NOT_FOUND::404')
res.status(200).json({success: true, data: key})
res.status(200).json({ success: true, data: key })
})
// COMPANY
app.get('/project/:pid(\\d+)/company', (req, res, next) => {
const where = req.query.id ? ' and id = ' + parseInt(req.query.id) : ''
const rows = db
function addCompany(data) {
return db
.prepare(`
select id, name, email, phone, description, logo,
(select json_chat_array(user_id) from company_users where company_id = c.id) users
insert into companies (project_id, name, address, email, phone, site, description, logo)
values (:project_id, :name, :address, :email, :phone, :site, :description, :logo)
returning id
`)
.pluck(true)
.get(data)
}
function getCompany(id, project_id) {
const row = db
.prepare(`
select id, name, address, email, phone, site, description, logo,
(select json_group_array(user_id) from company_users where company_id = c.id) users
from companies c
where project_id = :project_id ${where}
where c.id = :id and project_id = :project_id
order by name
`)
.get({id, project_id})
if (!row)
throw Error('NOT_FOUND::404')
row.users = JSON.parse(row.users || '[]')
return row
}
app.get('/project/:pid(\\d+)/company', (req, res, next) => {
const data = db
.prepare(`
select id, name, address, email, phone, site, description, logo,
(select company_id = c.id from projects where id = :project_id) is_own,
(select json_group_array(user_id) from company_users where company_id = c.id) users
from companies c
where project_id = :project_id
order by name
`)
.all(res.locals)
rows.forEach(row => row.users = JSON.parse(row.users || '[]'))
data.forEach(row => {
row.users = JSON.parse(row.users || '[]')
})
if (where && rows.length == 0)
throw Error('NOT_FOUND::404')
res.status(200).json({success: true, data: where ? rows[0] : rows})
res.status(200).json({success: true, data})
})
app.get('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => {
res.redirect(req.baseUrl + `/project/${req.params.pid}/company?id=${req.params.cid}`)
const data = getCompany(req.params.cid, req.params.pid)
res.status(200).json({success: true, data})
})
app.post('/project/:pid(\\d+)/company', (req, res, next) => {
res.locals.name = req.body?.name
res.locals.address = req.body?.address
res.locals.email = req.body?.email
res.locals.phone = req.body?.phone
res.locals.site = req.body?.site
res.locals.description = req.body?.description
res.locals.logo = req.body?.logo
const id = db
.prepare(`
insert into companies (project_id, name, email, phone, site, description, logo)
values (:project_id, :name, :email, :phone, :site, :description, :logo)
returning id
`)
.pluck(res.locals)
.get(res.locals)
res.redirect(req.baseUrl + `/project/${req.params.pid}/company?id=${id}`)
const id = addCompany(res.locals)
const data = getCompany(id, req.params.pid)
res.status(200).json({success: true, data})
})
app.put('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => {
res.locals.id = parseInt(req.params.cid)
res.locals.name = req.body?.name
res.locals.address = req.body?.address
res.locals.email = req.body?.email
res.locals.phone = req.body?.phone
res.locals.site = req.body?.site
res.locals.description = req.body?.description
res.locals.logo = req.body?.logo
const info = db
.prepareUpdate(
'companies',
['name', 'email', 'phone', 'site', 'description'],
['name', 'address', 'email', 'phone', 'site', 'description', 'logo'],
res.locals,
['id', 'project_id'])
.run(res.locals)
@@ -565,55 +691,24 @@ app.put('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => {
if (info.changes == 0)
throw Error('NOT_FOUND::404')
res.redirect(req.baseUrl + `/project/${req.params.pid}/company?id=${req.params.cid}`)
const data = getCompany(req.params.cid, req.params.pid)
res.status(200).json({success: true, data})
})
app.delete('/project/:pid(\\d+)/company/:cid(\\d+)', (req, res, next) => {
res.locals.company_id = parseInt(req.params.cid)
res.locals.company_id = req.params.cid
const info = db
.prepare(`delete from companies where id = :company_id and project_id = :project_id`)
.prepare(`
delete from companies
where id = :company_id and project_id = :project_id and
not exists(select company_id from projects where id = :project_id)`)
.run(res.locals)
if (info.changes == 0)
throw Error('NOT_FOUND::404')
res.status(200).json({success: true})
})
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 chats
where project_id = :project_id ${where}
`)
.all(res.locals)
if (where && rows.length == 0)
throw Error('NOT_FOUND::404')
res.status(200).json({success: true, data: where ? rows[0] : rows})
})
app.get('/project/:pid(\\d+)/chat/:gid(\\d+)', (req, res, next) => {
res.redirect(req.baseUrl + `/project/${req.params.pid}/chat?id=${req.params.uid}`)
})
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 chats set project_id = null where id = :chat_id and project_id = :project_id`)
.run(res.locals)
if (info.changes == 0)
throw Error('NOT_FOUND::404')
await bot.sendMessage(res.locals.chat_id, 'Чат удален из проекта')
res.status(200).json({success: true})
res.status(200).json({success: true, data: {id: req.params.cid}})
})
app.put('/project/:pid(\\d+)/company/:cid(\\d+)/user', (req, res, next) => {
@@ -655,7 +750,6 @@ app.put('/project/:pid(\\d+)/company/:cid(\\d+)/user', (req, res, next) => {
if (user_ids.some(user_id => !rows.contains(user_id)))
throw Error('USED_MEMBER::400')
db
.prepare(`delete from company_users where company_id = :company_id`)
.run(res.locals)
@@ -670,4 +764,140 @@ app.put('/project/:pid(\\d+)/company/:cid(\\d+)/user', (req, res, next) => {
res.status(200).json({success: true})
})
module.exports = app
app.get('/project/:pid(\\d+)/company/mapping', (req, res, next) => {
const data = db
.prepare(`
select company_id, json_group_array(show_to_id) show_to_ids
from company_mappings
where project_id = :project_id and company_id <> show_to_id
`)
.all(res.locals)
data.forEach(row => {
row.show_to_ids = JSON.parse(row.show_to_ids || '[]')
})
res.status(200).json({success: true, data})
})
app.put('/project/:pid(\\d+)/company/mapping', (req, res, next) => {
if(!(req.body instanceof Array))
throw Error('ARRAY_REQUIRED::500')
db
.prepare(`delete from company_mappings where project_id = :project_id`)
.run(res.locals)
req.body
.filter(row => Number.isInteger(row.company_id) && row.show_to_ids instanceof Array && row.show_to_ids.every(id => Number.isInteger(id)))
.forEach(row => {
row.show_to_ids.push(row.company_id)
const json_ids = row.show_to_ids.join(', ')
const check = db
.prepare(`select count(1) from companies where project_id = :project_id and id in (${json_ids}) `)
.get(res.locals)
if (check.count)
return log.error (Error('IGNORE: ' + JSON.stringify(row)))
const locals = {
project_ids: res.locals.project_id,
company_id: row.company_id,
json_ids
}
db
.prepare(`
insert into company_mappings (project_id, company_id, show_to_id) values
select :project_ids, :company_id, value from json_each(:json_ids)
`)
.run(locals)
})
res.status(200).json({ success: true })
})
// CHATS
function getChat(id, project_id) {
const row = db
.prepare(`
select id, name, telegram_id, is_channel, invite_link, description, logo, user_count, bot_can_ban
from chats
where id = :id and project_id = :project_id
`)
.get({id, project_id})
if (!row)
throw Error('NOT_FOUND::404')
row.is_channel = Boolean(row.is_channel)
row.bot_can_ban = Boolean(row.bot_can_ban)
return row
}
app.get('/project/:pid(\\d+)/chat', (req, res, next) => {
const data = db
.prepare(`
select id, name, telegram_id, is_channel, invite_link, description, logo, user_count, bot_can_ban
from chats
where project_id = :project_id
`)
.all(res.locals)
data.forEach(row => {
row.is_channel = Boolean(row.is_channel)
row.bot_can_ban = Boolean(row.bot_can_ban)
})
res.status(200).json({success: true, data})
})
app.get('/project/:pid(\\d+)/chat/:gid(\\d+)', (req, res, next) => {
const data = getChat(req.params.gid, req.params.pid)
res.status(200).json({ success: true, data })
})
app.delete('/project/:pid(\\d+)/chat/:gid(\\d+)', async (req, res, next) => {
try {
res.locals.chat_id = req.params.gid
const info = db
.prepare(`update chats set project_id = null where id = :chat_id and project_id = :project_id`)
.run(res.locals)
if (info.changes == 0)
throw Error('NOT_FOUND::404')
await bot.sendMessage(res.locals.chat_id, 'ON_CHAT_REMOVE')
res.status(200).json({success: true, data: {id: req.params.gid}})
} catch (err){
next(err)
}
})
eventBus.on('chat-attached', chat => {
const customer_id = db
.prepare(`select customer_id from projects where id = :project_id`)
.pluck(true)
.get(chat)
if (!customer_id)
return
const msg = {
event: 'chat-attached',
entity: 'chat',
id: chat.id,
data: getChat(chat.id, chat.project_id)
}
Object.values(sessions)
.filter(s => s.customer_id == customer_id && s.ws?.readyState === WebSocket.OPEN)
.map(s => s.ws)
.forEach(ws => ws.send(JSON.stringify(msg)))
})
module.exports = { router: app, registerWS }