diff --git a/backend/_old/backend.zip b/backend/_old/backend.zip new file mode 100644 index 0000000..48f165a Binary files /dev/null and b/backend/_old/backend.zip differ diff --git a/backend/api (2).xls b/backend/api (2).xls new file mode 100644 index 0000000..34927f9 Binary files /dev/null and b/backend/api (2).xls differ diff --git a/backend/app.js b/backend/app.js index 8f5d343..f2a6b18 100644 --- a/backend/app.js +++ b/backend/app.js @@ -15,7 +15,7 @@ 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,29 +26,17 @@ app.use((req, res, next) => { .map(key => req.body[key] = escapeHtml(req.body[key])) next() -}) +}) */ -// cors -app.use((req, res, next) => { - res.set({ - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE', - 'Access-Control-Allow-Headers': 'Accept,Accept-Language,Content-Language,Content-Type,Authorization,Cookie,X-Requested-With,Origin,Host', - 'Access-Control-Allow-Credentials': true - }) - - return req.method == 'OPTIONS' ? res.status(200).json({success: true}) : next() -}) - -app.post('(/api/admin/customer/login|/api/miniapp/user/login)', (req, res, next) => { +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 dataCheckString = Object.keys(data).sort().map((key) => `${key}=${data[key]}`).join("\n") - const secretKey = crypto.createHmac("sha256", "WebAppData").update(BOT_TOKEN).digest() - const hmac = crypto.createHmac("sha256", secretKey).update(dataCheckString).digest("hex") + const dataCheckString = Object.keys(data).sort().map((key) => `${key}=${data[key]}`).join('\n') + const secretKey = crypto.createHmac('sha256', 'WebAppData').update(BOT_TOKEN).digest() + const hmac = crypto.createHmac('sha256', secretKey).update(dataCheckString).digest('hex') const timeDiff = Date.now() / 1000 - data.auth_date @@ -73,14 +61,9 @@ app.use((err, req, res, next) => { console.error(`Error for ${req.path}: ${err}`) let message, code - //if (err.code == 'SQLITE_ERROR' || err.code == 'SQLITE_CONSTRAINT_CHECK') { - // message = 'DATABASE_ERROR' - //code = err.code == 'SQLITE_CONSTRAINT_CHECK' ? 400 : 500 - //} else { - [message, code = 500] = err.message.split('::') - //} + [message, code = 500] = err.message.split('::') - res.status(res.statusCode == 200 ? 500 : res.statusCode).json({success: false, error: { message, code}}) + res.status(code).json({success: false, error: { message, code}}) }) app.use(express.static('public')) diff --git a/backend/apps/admin.js b/backend/apps/admin.js index 854a091..baafc63 100644 --- a/backend/apps/admin.js +++ b/backend/apps/admin.js @@ -15,11 +15,10 @@ const upload = multer({ }) const sessions = {} +const emailCache = {} // key = email, value = code app.use((req, res, next) => { - if (req.path == '/customer/login' || - req.path == '/customer/register' || - req.path == '/customer/activate') + if (req.path == '/auth/email' || req.path == '/auth/telegram' || req.path == '/auth/register' || req.path == '/auth/logout') return next() const asid = req.query.asid || req.cookies.asid @@ -31,96 +30,104 @@ app.use((req, res, next) => { next() }) -// CUSTOMER -app.post('/customer/login', (req, res, next) => { +// AUTH +function createSession(req, res, customer_id) { + if (!customer_id) + throw Error('AUTH_ERROR::500') + + res.locals.customer_id = customer_id + const asid = crypto.randomBytes(64).toString('hex') + req.session = sessions[asid] = {asid, customer_id } + res.setHeader('Set-Cookie', [`asid=${asid};httpOnly;path=/api/admin`]) +} + +app.post('/auth/email', (req, res, next) => { res.locals.email = req.body?.email res.locals.password = req.body?.password - let customer_id = db - .prepare(` - select id - from customers - where is_active = 1 and ( - email is not null and email = :email and password is not null and password = :password or - email is null and password is null and telegram_user_id = :telegram_id - ) - `) + const customer_id = db + .prepare(`select id from customers where is_blocked = 0 and email = :email and password is not null and password = :password `) .pluck(true) .get(res.locals) - if (!customer_id && !res.locals.email && !res.locals.password) { - customer_id = db - .prepare(`insert into customers (telegram_user_id, is_active) values (:telegram_id, 1) returning id`) - .safeIntegers(true) - .pluck(true) - .get(res.locals) - } - if (!customer_id) throw Error('AUTH_ERROR::401') - res.locals.customer_id = customer_id - db - .prepare(`update customers set telegram_user_id = :telegram_id where id = :customer_id and email is not null`) - .run(res.locals) - - const asid = crypto.randomBytes(64).toString('hex') - req.session = sessions[asid] = {asid, customer_id } - res.setHeader('Set-Cookie', [`asid=${asid};httpOnly;path=/api/admin`]) - + createSession(req, res, customer_id) res.status(200).json({success: true}) }) -app.get('/customer/logout', (req, res, next) => { - delete sessions[req.session.asid] - res.setHeader('Set-Cookie', [`asid=; expired; httpOnly`]) +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`) + .pluck(true) + .get(res.locals) || db + .prepare(`replace into customers (telegram_id, is_blocked) values (:telegram_id, 0) returning id`) + .pluck(true) + .get(res.locals) + + createSession(req, res, customer_id) res.status(200).json({success: true}) }) -app.post('/customer/register', (req, res, next) => { - const email = String(req.body.email).trim() - const password = String(req.body.password).trim() +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}) +}) - 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)) +app.post('/auth/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') - - if (!password) - throw Error('EMPTY_PASSWORD::400') - - const row = db + + const customer_id = db .prepare('select id from customers where email = :email') - .run({email}) - if (row) - throw Error('DUPLICATE_EMAIL::400') + .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 key = crypto.randomBytes(32).toString('hex') - const info = db - .prepare('insert into customers (email, password, activation_key) values (:email, :password, :key)') - .run({email, password, key}) - - // To-Do: SEND MAIL - console.log(`http://127.0.0.1:3000/api/customer/activate?key=${key}`) - res.status(200).json({success: true, data: key}) -}) - -app.get('/customer/activate', (req, res, next) => { - const row = db - .prepare('update customers set is_active = 1 where activation_key = :key returning id') - .get({key: req.query.key}) - - if (!row || !row.id) - throw Error('BAD_ACTIVATION_KEY::400') - - 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 from customers - where id = :customer_id and is_active = 1 + where id = :customer_id `) .get(res.locals) diff --git a/backend/apps/bot.js b/backend/apps/bot.js index 963680c..17c9c71 100644 --- a/backend/apps/bot.js +++ b/backend/apps/bot.js @@ -10,8 +10,9 @@ const { NewMessage } = require('telegram/events') const { Button } = require('telegram/tl/custom/button') const { CustomFile } = require('telegram/client/uploads') -// const session = new StringSession('1AgAOMTQ5LjE1NC4xNjcuNTABuxdIxmjimA0hmWpdrlZ4Fo7uoIGU4Bu9+G5QprS6zdtyeMfcssWEZp0doLRX/20MomQyF4Opsos0El0Ifj5aiNgg01z8khMLMeT98jS+1U/sh32p3GxZfxyXSxX1bD0NLRaXnqVyNNswYqRZPhboT28NMjDqwlz0nrW9rge+QMJDL7jIkXgSs+cmJBINiqsEI8jWjXmc8TU/17gngtjUHRf5kRM4y5gsNC4O8cF5lcHRx0G/U5ZVihTID8ItQ6EdEHjz6e4XErbVOJ81PfYkqEoPXVvkEmRM0/VbvCzFfixfas4Vzczfn98OHLd8P2MXcgokZ2rppvIV3fQXOHxJbA0=') -const session = new StringSession('') +//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=') + let client @@ -493,12 +494,19 @@ async function onNewUserMessage (msg) { updateUser(userId, user) await updateUserPhoto (userId, user) + const appButton = new Api.KeyboardButtonWebView({ + text: "Open Mini-App", // Текст на кнопке + url: "https://h5sj0gpz-3000.euw.devtunnels.ms/", // URL вашего Mini-App (HTTPS!) + }); + + const inputPeer = new Api.InputPeerUser({userId: tgUserId, accessHash: user.accessHash.value}) const resultBtn = await client.sendMessage(inputPeer, { message: 'Сообщение от бота', buttons: client.buildReplyMarkup([ [Button.url('Админка', 'https://t.me/ready_or_not_2025_bot/userapp?startapp=admin')], - [Button.url('Пользователь', 'https://t.me/ready_or_not_2025_bot/userapp?startapp=user')] + [Button.url('Пользователь', 'https://t.me/ready_or_not_2025_bot/userapp?startapp=user')], + [appButton] ]) }) } catch (err) { @@ -560,6 +568,7 @@ class Bot extends EventEmitter { }) await client.start({botAuthToken}) + console.log('SID: ', session.save()) } async uploadDocument(projectId, fileName, mime, data, parentType, parentId, publishedBy) { diff --git a/backend/data/db.sqlite b/backend/data/db.sqlite index 438da8f..9ad2084 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 index 3d2446d..41f2610 100644 Binary files a/backend/data/db.sqlite-shm and b/backend/data/db.sqlite-shm differ diff --git a/backend/data/db.sqlite-wal b/backend/data/db.sqlite-wal index 1d97cbf..53d4572 100644 Binary files a/backend/data/db.sqlite-wal and b/backend/data/db.sqlite-wal differ diff --git a/backend/data/init.sql b/backend/data/init.sql index 675cc28..841b7ff 100644 --- a/backend/data/init.sql +++ b/backend/data/init.sql @@ -5,11 +5,10 @@ create table if not exists customers ( name text check(name is null or trim(name) <> '' and length(name) < 256), email text check(email is null or trim(email) <> '' and length(email) < 128), password text check(password is null or length(password) > 7 and length(password) < 64), - telegram_user_id integer, + telegram_id integer, plan integer, json_balance text default '{}', - activation_key text, - is_active integer default 0, + is_blocked integer default 0, json_company text default '{}', upload_group_id integer, json_backup_server text default '{}', diff --git a/backend/letsgo.bat b/backend/letsgo.bat new file mode 100644 index 0000000..d831440 --- /dev/null +++ b/backend/letsgo.bat @@ -0,0 +1 @@ +node app \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index d1bef8c..26bb085 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -392,9 +392,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -1169,9 +1169,9 @@ } }, "node_modules/nodemailer": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", - "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" diff --git a/backend/public/index.html b/backend/public/index.html index 254ff81..df4399f 100644 --- a/backend/public/index.html +++ b/backend/public/index.html @@ -8,7 +8,7 @@ - +