diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..d95e46c --- /dev/null +++ b/.env-example @@ -0,0 +1,10 @@ +PORT=9090 +APP_URL=http://localhost:9090 +APP_API=https://cluster-01.apigratis.com/api +JWT_SECRET=MY_SECRET_KEY +JWT_ALGO=HS256 + +SecretKey= +PublicToken= +DeviceToken= +Authorization= \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/README.md b/README.md new file mode 100644 index 0000000..283f635 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# apigratis-chatopen + diff --git a/middleware/JWTCheck.js b/middleware/JWTCheck.js new file mode 100644 index 0000000..b4008d2 --- /dev/null +++ b/middleware/JWTCheck.js @@ -0,0 +1,32 @@ +const jwt = require('jsonwebtoken'); + +function JWTCheck(req, res, next) { + // Pega o token do header da requisição + // const token = req.headers.authorization && req.headers.authorization.split(' ')[1]; + + //get all cookies headers + const cookies = req.headers.cookie; + + //get token from cookies + const token = cookies && cookies.split(';').find(c => c.trim().startsWith('token='))?.split('=')[1]; + + console.log('token', token); + + if (!token) { + return res.status(401).redirect('/auth'); + } + + try { + // Verifica o token usando a chave secreta + const decoded = jwt.verify(token, process.env.JWT_SECRET); + // Adiciona os dados do usuário decodificados à requisição para uso nas próximas rotas + req.userData = decoded; + next(); + } catch (error) { + return res.status(401).redirect('/auth')-send({ + message: 'Falha na autenticação do token' + }); + } +} + +module.exports = JWTCheck; \ No newline at end of file diff --git a/ngrok.exe b/ngrok.exe new file mode 100644 index 0000000..7a75865 Binary files /dev/null and b/ngrok.exe differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..3705e0e --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "apigratis-socket", + "version": "0.0.1", + "description": "socket central apibrasil", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.3.3", + "body-parser": "^1.20.1", + "dotenv": "^16.0.3", + "express": "^4.16.3", + "jsonwebtoken": "^8.3.0", + "socket.io": "^2.1.1" + } +} diff --git a/public/assets/auth/auth.css b/public/assets/auth/auth.css new file mode 100644 index 0000000..4f798df --- /dev/null +++ b/public/assets/auth/auth.css @@ -0,0 +1,133 @@ +body{margin-top:20px;} + +body.login { + position: initial; + background: url('https://www.bootdey.com/template_demo/dayfriend/img/Bg/faces2.png'); + padding-top: 0; +} + +body.login #content { + max-width: 760px; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +body.login .lock-container { + margin: auto; + width: 300px; +} + +@media (min-width: 480px) { + body.login .lock-container { + bottom: 0; + height: 550px; + left: 0; + position: absolute; + top: 0; + right: 0; + } +} + +body.login .lock-container h1 { + text-align: center; + color: #fff; + font-size: 28px; +} + +body.login .lock-container .panel { + max-width: 300px !important; + background: #fff; + position: relative; +} + +body.login .lock-container .panel img { + margin: 20px 0 10px; +} + +body.login .lock-container .panel input { + margin-bottom: 10px; +} + +body.login .lock-container .panel .btn { + margin-top: 10px; +} + + +body.login .lock-container .panel .forgot-password { + margin: 10px 0 0; + font-weight: 500; + color: #26a69a; + display: block; +} + +body.login #content { + max-width: 760px; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +#content { + min-width: 320px; +} +/* Sticky footer styles +-------------------------------------------------- */ +html { + position: relative; + min-height: 100%; +} + +body { + /* Margin bottom by footer height */ + margin-bottom: 60px; +} + +.footer { + position: absolute; + bottom: 0; + width: 100%; + /* Set the fixed height of the footer here */ + height: 60px; + background-color: #f5f5f5; +} + +body.login .lock-container .panel input { + margin-bottom: 10px; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #333333; + background-color: #f7f7f7; + background-image: none; + border: 1px solid #efefef; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} + +.footer-container { + width: auto; + max-width: 680px; + padding: 0 15px; +} +.footer-container .text-muted { + margin: 20px 0; +} + +.img-login { + width:120px; + height:120px; +} \ No newline at end of file diff --git a/public/assets/auth/auth.js b/public/assets/auth/auth.js new file mode 100644 index 0000000..d7ad3f6 --- /dev/null +++ b/public/assets/auth/auth.js @@ -0,0 +1,43 @@ +window.addEventListener('load', () => { + //body add class login breakpoint-1024 + $("body").addClass("login breakpoint-1024"); +}); + +const form = document.querySelector('form'); + +form.addEventListener('submit', async (e) => { + e.preventDefault(); + + const email = form.email.value; + const password = form.password.value; + + const response = await fetch('/api/auth', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ email, password }) + }); + + const data = await response.json(); + + console.log(data); + + if (data.error == true ) { + + $('.alert-danger').css('display', 'block'); + $('.alert').text(data.message); + + } else { + + // const cellphone = data?.user?.cellphone; + let ddi = `55`; + let ddd = data?.user?.cellphone?.substring(0, 2); + let cellphone = data?.user?.cellphone?.substring(2, 11); + + let mobile = `${ddi}${ddd}${cellphone}`; + + window.location.href = `/chat?welcome=true&session=${mobile}` + + } +}); \ No newline at end of file diff --git a/public/assets/chat/chat.css b/public/assets/chat/chat.css new file mode 100644 index 0000000..0b4d931 --- /dev/null +++ b/public/assets/chat/chat.css @@ -0,0 +1,800 @@ +@import url('http://fonts.googleapis.com/css2?family=Poppins:wght@300&display=swap'); +@import url('http://fonts.googleapis.com/css2?family=Poppins:wght@400&display=swap'); +@import url('http://fonts.googleapis.com/css2?family=Poppins:wght@50&display=swap'); + +body { + font-size: 14px; + font-weight: 300; + color: rgb(58, 65, 111); + background-image: linear-gradient(to right, rgba(238, 240, 248, 0.5), rgb(238, 240, 248), rgba(238, 240, 248, 0.5)); + font-family: Poppins !important; + margin: 0px; +} + +html body a { + color: rgb(0, 115, 152); + cursor: pointer; + transition: all 0.2s linear 0s; +} + +.container { + padding-right: 0; + padding-left: 0; +} + +@media (min-width: 1400px) { + .container { + max-width: 1320px !important; + } +} + +.card-stacked { + display: flex; + flex-flow: row wrap; +} + +.chat { + position: relative; +} + +.chat .chat-user-detail { + position: absolute; + left: 0px; + width: 0px; + opacity: 0; + z-index: -4; +} + +.chat .chat-header { + border-bottom: 1px solid rgb(225, 225, 227); + background: rgb(255, 255, 255); +} + +.margin-auto { + margin-top: auto !important; + margin-bottom: auto !important; +} + +.btn.btn-light-light:not(:disabled):not(.disabled).active, .btn.btn-light-light:not(:disabled):not(.disabled):active, .btn.btn-light:not(:disabled):not(.disabled).active, +.btn.btn-light:not(:disabled):not(.disabled):active, .show>.btn.btn-light-light.dropdown-toggle, .show>.btn.btn-light.dropdown-toggle { + color: rgb(126, 130, 153); + background-color: rgb(238, 240, 249); + border-color: rgb(238, 240, 249); +} + +.feather { + color: rgb(61, 81, 167); + fill: rgba(108, 124, 195, 0.15); +} + +.avatar-xxl { + width: 110px; + height: 110px; +} + +.animate4 { + animation: 3s cubic-bezier(0.1, 0.82, 0.25, 1) 0s 1 normal none running animate4; +} + +.fw-300 { + font-weight: 300 !important; +} + +.h6, h6 { + font-size: 1.175rem; +} + +.btn.btn-icon.btn-sm { + height: 30px; + width: 30px; +} + +.btn.btn-icon.btn-sm i { + font-size: 1.2rem; +} + +.btn.btn-icon i { + font-size: 1.35rem; +} + +a.btn i, button.btn i { + font-size: 1rem; + vertical-align: middle; +} + +.btn.btn-light-skype { + background-color: rgba(0, 175, 240, 0.125); + color: rgb(0, 175, 240) !important; +} + +.btn.btn-light-facebook { + background-color: rgba(59, 89, 152, 0.125); + color: rgb(59, 89, 152) !important; +} + +.btn.btn-light-twitter { + background-color: rgba(29, 161, 242, 0.125); + color: rgb(29, 161, 242) !important; +} + +.btn.btn-light-instagram { + background-color: rgba(225, 48, 108, 0.125); + color: rgb(225, 48, 108) !important; +} + +.btn.btn-icon { + height: calc(1.5em + 1.2rem); + width: calc(1.5em + 1.2rem); + color: rgb(255, 255, 255); + display: inline-flex; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: center; + justify-content: center; + cursor: pointer; + padding: 0px; +} + +.btn-circle, .flag-circle { + border-radius: 50% !important; +} + +.btn-shadow { + box-shadow: rgba(50, 50, 93, 0.11) 0px 4px 6px, rgba(0, 0, 0, 0.08) 0px 1px 3px; +} + +.btn-group-sm>.btn, .btn-sm { + font-size: 0.725rem; + line-height: 1.35; + padding: 0.45rem 0.75rem; + border-radius: 0.4rem; +} + +.btn { + font-size: 0.875rem; + font-weight: 400 !important; + border-radius: 0.4rem; + border-width: 1px; + border-style: solid; + border-color: transparent; + border-image: initial; + padding: 0.5rem 1rem; +} + +.chat .chat-profile-picture { + cursor: pointer; +} + +.chat-search { + padding-top: 9px; + padding-bottom: 9px; + background: rgb(241, 242, 250); + border-bottom: 1px solid rgb(225, 225, 227); +} + +.chat-search .input-group { + position: relative; + box-shadow: rgb(242, 242, 246) 0px 0px 0px 2px !important; + border-radius: 8px; + background: rgb(238, 240, 249); +} + +.form-control input, .form-group input, input { + box-shadow: none; + font-size: 0.9rem; + font-weight: 300 !important; + border-radius: 5px; + border-width: 1px; + border-style: solid; + border-color: rgb(222, 226, 230); + border-image: initial; +} + +.avatar-sm { + height: calc(1.5em + 1.2rem) !important; + width: calc(1.5em + 1.2rem) !important; +} + +.btn.btn-light:hover { + color: rgb(63, 66, 84); + background-color: rgb(228, 230, 239); +} + +.btn.focus, .btn:focus, .form-control:focus, .form-group input:focus, .form-group:focus, input:focus { + box-shadow: rgba(101, 118, 255, 0.1) 0px 0px 0px 3px; + border-color: rgba(101, 118, 255, 0); + outline: 0px; +} + +.card { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, .125); + border-radius: 0.25rem; +} + +.chat-search .input-group-text i { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + cursor: pointer; +} + +.drop-shadow { + filter: drop-shadow(0 0 1px #504c4c54); +} + +.fs-17 { + font-size: 17px !important; +} + +.chat-search .input-group-text { + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + position: relative; + padding-top: 0; + padding-bottom: 0; +} + +.input-group>.input-group-append>.btn, .input-group>.input-group-append>.input-group-text, .input-group>.input-group-prepend:first-child>.btn:not(:first-child), .input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child), .input-group>.input-group-prepend:not(:first-child)>.btn, .input-group>.input-group-prepend:not(:first-child)>.input-group-text { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.chat-search .input-group-text, .chat-search input { + font-size: 14px; + box-shadow: unset !important; + border-width: initial; + border-style: initial; + border-color: transparent; + border-image: initial; + background: rgb(255, 255, 255); + border-radius: 8px; +} + +.chat-search input { + border-top-right-radius: 0px !important; + border-bottom-right-radius: 0px !important; +} + +.prepend-white .input-group-text { + background-color: #fff; +} + +.input-group-text { + font-size: unset; + font-weight: unset; + background-color: #eef0fc; +} + +.pr-2, .px-2 { + padding-right: 0.5rem !important; +} + +.input-group-text { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.archived-messages { + cursor: pointer; +} + +.archived { + background-image: url(https://user-images.githubusercontent.com/35243461/168796895-2fc5e22b-06db-46b5-9854-d517778cfe57.svg); +} + +.svg15 { + height: 15px; + width: 15px; +} + +.fw-400 { + font-weight: 400 !important; +} + +.text-dark-75 { + color: #3f4254 !important; +} + +.chat-user-panel { + border-top: 1px solid #eef0f9; + cursor: pointer; +} + +.chat-user-scroll { + max-height: 620px; + position: relative; +} + +.chat .chat-item { + border-bottom-width: 1px; + border-bottom-style: dashed; + border-bottom-color: #e4e6ef; +} + +img.shadow { + box-shadow: 0 15px 35px rgba(50, 50, 93, .15), 0 5px 15px rgba(0, 0, 0, .15) !important; +} + +.double-check { + background-image: url(https://user-images.githubusercontent.com/35243461/168796897-1db3c9e8-c8e4-47b5-8399-9ee230bcd35f.svg); +} + +.chat .chat-item .message-shortcut { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; +} + +.fs-13 { + font-size: 13px !important; +} + +.fs-15 { + font-size: 15px !important; +} + +.pinned { + background-image: url(https://user-images.githubusercontent.com/35243461/168796901-94490a54-e25d-4094-a91d-38f357898ad6.svg); +} + +.svg18 { + height: 18px; + width: 18px; +} + +.chat .chat-item.active, .chat .chat-item:hover { + background-color: #f5f8fa; +} + +.badge.round { + border-radius: 1.5rem; +} + +.badge-light-success { + background-color: rgba(10, 187, 135, .1); + color: #0abb87; +} + +.badge { + font-weight: 500; + display: inline-block; + padding: 0.4em 1.2em; + font-size: 80% !important; + text-align: center; + vertical-align: baseline; + border-radius: 0.25rem; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; +} + +.single-check { + background-image: url(https://user-images.githubusercontent.com/35243461/168796902-03e56437-61b1-48a5-a55e-3dcc41a09deb.svg); +} + +.double-check-blue { + background-image: url(https://user-images.githubusercontent.com/35243461/168796899-499f66ce-ed05-404d-8f37-063165788b31.svg); +} + +::-webkit-input-placeholder { + font-size: .8rem +} + +::-moz-placeholder { + font-size: .8rem +} + +:-ms-input-placeholder { + font-size: .8rem +} + +.shadow-line { + box-shadow: rgba(62, 57, 107, 0.07) 0px 1px 15px 1px; + border-width: 1px; + border-style: solid; + border-color: rgba(211, 218, 230, 0.43); + border-image: initial; +} + +.text-small { + font-size: 12px !important; +} + +.chat-panel-scroll { + max-height: 560px; + position: relative; +} + +.horizontal-margin-auto { + margin-right: auto !important; + margin-left: auto !important; +} + +.loader-animate3 { + background-image: url(https://user-images.githubusercontent.com/35243461/168796900-207c2e2a-4a21-4ef0-9dc7-fd82e3e20073.svg); +} + +.svg36 { + height: 36px; + width: 36px; +} + +.letter-space { + letter-spacing: 1.5px; +} + +.fs-12 { + font-size: 12px !important; +} + +.chat.chat-panel .left-chat-message, .chat.chat-panel .right-chat-message { + padding: 0.5rem 1rem; + max-width: 47%; +} + +.left-chat-message { + position: relative; + margin: 0 0 0 10px; + background: #fff; + width: fit-content; + max-width: 60%; + padding: 0.7rem 1rem; + border-radius: 0.357rem; + -webkit-box-shadow: 0 4px 8px 0 rgb(34 41 47 / 3%); + box-shadow: 0 4px 8px 0 rgb(34 41 47 / 3%); +} + +.chat.chat-panel .message-arrow, .chat.chat-panel .message-time { + position: absolute; + right: 5px; + bottom: 5px; + padding: 2px 6px; + font-size: 11px; + color: #6c757d; + cursor: pointer; +} + +.chat.chat-panel .message-arrow, .chat.chat-panel .message-time { + position: absolute; + right: 5px; + bottom: 5px; + padding: 2px 6px; + font-size: 11px; + color: #6c757d; + cursor: pointer; +} + +.chat.chat-panel .message-arrow { + transform: scale(0); +} + +.chat.chat-panel { + background: linear-gradient(to bottom, #e9e4f07d, #d3cce34f); +} + +.chat { + position: relative; +} + +.chat.chat-panel .left-chat-message, .chat.chat-panel .right-chat-message { + padding: 0.5rem 1rem; + max-width: 47%; +} + +.chat.chat-panel .left-chat-message, .chat.chat-panel .right-chat-message { + padding: 0.5rem 1rem; + max-width: 47%; +} + +.right-chat-message { + position: relative; + margin: 0 10px 0 0; + background: linear-gradient(to right, #348ac7, #7474bf); + color: #fff; + width: fit-content; + max-width: 60%; + padding: 0.7rem 1rem; + border-radius: 0.357rem; + -webkit-box-shadow: 0 4px 8px 0 rgb(34 41 47 / 12%); + box-shadow: 0 4px 8px 0 rgb(34 41 47 / 12%); +} + +.chat.chat-panel .message-options.dark .message-arrow i, .chat.chat-panel .message-options.dark .message-time { + color: #fff !important; +} + +.chat.chat-panel .chat-upload-trigger { + position: relative; +} + +.chat.chat-panel .chat-upload.active { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + transform: scale(1); + bottom: 50px; +} + +.chat.chat-panel .chat-upload { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + transform: scale(0); + position: absolute; + left: -5px; + bottom: -50px; +} + +.btn.btn-light-secondary.btn-blushing, .btn.btn-secondary.btn-blushing { + box-shadow: 1px 1px 1px 1px rgb(117 106 208 / 40%); +} + +.btn.btn-secondary { + color: #fff; + background-color: #8950fc; + border: 0; +} + +.btn.btn-danger.btn-blushing, .btn.btn-light-danger.btn-blushing { + box-shadow: 1px 1px 1px 1px #fd397a5c; +} + +.btn.btn-light-warning.btn-blushing, .btn.btn-warning.btn-blushing { + box-shadow: 1px 1px 1px 1px rgb(255 184 34 / 40%); +} + +.btn.btn-light-success.btn-blushing, .btn.btn-success.btn-blushing { + box-shadow: 1px 1px 1px 1px rgb(10 187 135 / 40%); +} + +.btn.btn-light-secondary.btn-blushing, .btn.btn-secondary.btn-blushing { + box-shadow: 1px 1px 1px 1px rgb(117 106 208 / 40%); +} + +.chat-search .input-group-text i { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + cursor: pointer; +} + +.chat-search .input-group-text i:hover { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + -webkit-transform: translateY(-2px) scale(1); + transform: translateY(-2px) scale(1); +} + +.fs-19 { + font-size: 19px !important; +} + +.chat.chat-panel .left-chat-message, .chat.chat-panel .right-chat-message { + padding: .5rem 1rem; + max-width: 47% +} + +.chat.chat-panel .message-arrow { + transform: scale(0); +} + +.chat.chat-panel .left-chat-message:hover .message-time, .chat.chat-panel .right-chat-message:hover .message-time { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + transform: scale(0); +} + +.chat.chat-panel .left-chat-message:hover .message-arrow, .chat.chat-panel .right-chat-message:hover .message-arrow { + -webkit-transition: all .25s ease 0s; + transition: all .25s ease 0s; + transform: scale(1); +} + +.chat .chat-user-detail { + position: absolute; + left: 0; + width: 0; + opacity: 0; + z-index: -4; +} + +.chat .chat-user-detail.active { + -webkit-transition: all .25s ease 0s; + transition: all .4s cubic-bezier(1, .04, 0, .93) 0s; + height: 100%; + width: 100%; + background: #f1f2fa; + z-index: 1; + opacity: 1; +} + +.card { + margin-bottom: 1.875rem; + border: none; + box-shadow: 0 1px 2px #00000030; + border-radius: 0.45rem; + width: 100%; +} + +.btn.btn-light-skype.focus, .btn.btn-light-skype:focus, .btn.btn-light-skype:hover:not(:disabled):not(.disabled) { + color: #fff !important; + background-color: #00aff0; + border-color: #00aff0; +} + +.btn.btn-light-facebook.focus, .btn.btn-light-facebook:focus, .btn.btn-light-facebook:hover:not(:disabled):not(.disabled) { + color: #fff !important; + background-color: #3b5998; + border-color: #3b5998; +} + +.btn.btn-light-twitter.focus, .btn.btn-light-twitter:focus, .btn.btn-light-twitter:hover:not(:disabled):not(.disabled) { + color: #fff !important; + background-color: #1da1f2; + border-color: #1da1f2; +} + +.btn.btn-light-instagram.focus, .btn.btn-light-instagram:focus, .btn.btn-light-instagram:hover:not(:disabled):not(.disabled) { + color: #fff !important; + background-color: #e1306c; + border-color: #e1306c; +} + +/*perfect scrollbar*/ +.ps-container { + -ms-touch-action: none; + touch-action: none; + overflow: auto !important; + -ms-overflow-style: none +} + +@supports (-ms-overflow-style:none) { + .ps-container { + overflow: auto !important + } +} + +#messages{ + min-height: 650px; +} + +@media screen and (-ms-high-contrast:active), (-ms-high-contrast:none) { + .ps-container { + overflow: auto !important + } +} + +.ps-container.ps-active-x>.ps-scrollbar-x-rail, .ps-container.ps-active-y>.ps-scrollbar-y-rail { + display: block; + background-color: transparent +} + +.ps-container.ps-in-scrolling { + pointer-events: none +} + +.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail { + background-color: #eee; + opacity: .9 +} + +.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x { + background-color: #999 +} + +.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail { + background-color: #eee; + opacity: .9 +} + +.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y { + background-color: #999 +} + +.ps-container>.ps-scrollbar-x-rail { + display: none; + position: absolute; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + opacity: 0; + bottom: 3px; + height: 8px +} + +.ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x { + position: absolute; + background-color: #aaa; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + bottom: 0; + height: 4px +} + +.ps-container>.ps-scrollbar-y-rail { + display: none; + position: absolute; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + opacity: 0; + right: 3px; + width: 4px +} + +.ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y { + position: absolute; + background-color: #a2adb7; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + right: 0; + width: 4px +} + +.ps-container:hover.ps-in-scrolling { + pointer-events: none +} + +.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail { + background-color: #eee; + opacity: .9 +} + +.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x { + background-color: #a2adb7 +} + +.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail { + background-color: #eee; + opacity: .9 +} + +.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y { + background-color: #a2adb7 +} + +.ps-container:hover>.ps-scrollbar-x-rail, .ps-container:hover>.ps-scrollbar-y-rail { + opacity: .6 +} + +.ps-container:hover>.ps-scrollbar-x-rail:hover { + background-color: #eee; + opacity: .9 +} + +.ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x { + background-color: #a2adb7 +} + +.ps-container:hover>.ps-scrollbar-y-rail:hover { + background-color: #eee; + opacity: .9 +} + +.ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y { + background-color: #a2adb7 +} diff --git a/public/assets/chat/chat.js b/public/assets/chat/chat.js new file mode 100644 index 0000000..ec842fd --- /dev/null +++ b/public/assets/chat/chat.js @@ -0,0 +1,396 @@ + +//parametro sessao get +const urlParams = new URLSearchParams(window.location.search); +const channelName = urlParams?.get('session')?.split(' ')?.join('-')?.toLowerCase()?.trim()?.toLowerCase(); +const contact = urlParams?.get('contact')?.split(' ')?.join('-')?.toLowerCase()?.trim()?.toLowerCase(); + +// Connect to the server +const bearer = localStorage.getItem('token') +const socket = io('http://localhost:9090', { + query: { + bearer: bearer, + channelName: channelName + } +}); + +// Get references to DOM elements +const messages = document.getElementById('messages'); +const chatlist = document.getElementsByClassName('chat-list')[0]; +const messageInput = document.getElementById('messageInput'); +const sendButton = document.getElementById('sendButton'); + +console.log(`channelName: ${channelName}`); + +//window loadd request get async fetch +window.addEventListener('load', async (event) => { + + //featch /api/profile/number + const response_profile = await fetch(`http://localhost:9090/api/profile/${channelName}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + const data_profile = await response_profile.json(); + + console.log('data_profile:', data_profile); + + // parameter welcome == true + //chat-header display none + if(urlParams?.get('welcome') === 'true') { + + $('.chat-header-1').css('display', 'none'); + $('.chat-message').css('display', 'none'); + + //chat-history height 600px + $('.chat-history').css('height', '550px'); + //chat-history background image https://w0.peakpx.com/wallpaper/818/148/HD-wallpaper-whatsapp-background-cool-dark-green-new-theme-whatsapp.jpg + $('.chat-history').css('background-image', 'url(https://conteudo.imguol.com.br/c/noticias/2015/01/23/logo-whatsapp-whatsapp-na-web-1422023070243_300x300.jpg)'); + //baackground color #000 + $('.chat-history').css('background-color', 'rgb(252 252 253)'); + //background-size cover opacity 0.5 + $('.chat-history').css('background-size', 'auto'); + $('.chat-history').css('background-repeat', 'no-repeat'); + $('.chat-history').css('background-position', 'center'); + $('.chat-history').css('opacity', '0.5'); + + } + + const response = await fetch(`http://localhost:9090/api/chats/${channelName}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await response.json(); + console.log('data:', data); + + if (data?.response?.contacts?.length > 0) { + chatlist.innerHTML = ''; + data?.response?.contacts?.forEach((item) => { + + console.log('item:', item); + + //id author user + if(item?.isUser === true) { + + let content_html = `
+
+
+ avatar +
+

${ item?.contact?.formattedName?.length > 15 ? item?.contact?.formattedName?.substring(0, 15) + '...' : item?.contact?.formattedName }

+
+ + 📱 ${ item?.contact.id?.user } + + +
+
+
+
+
+
+

08:55

+ + ${ item?.unreadCount > 0 ? ` ${item?.unreadCount}
` : '' } +
+
+ + ` + + // let content_html = `
  • + // avatar + //
    + //
    ${ item?.contact?.formattedName?.length > 15 ? item?.contact?.formattedName?.substring(0, 15) + '...' : item?.contact?.formattedName }
    + //
    ${item?.contact?.id?.user}
    + //
    + //
  • ` + + chatlist.innerHTML += content_html; + } + }); + + //if contact parameter add class active + if (contact) { + document.getElementById(contact)?.classList.add('active'); + + //click + document.getElementById(contact)?.click(); + //fish scrool down + + } + + }else{ + chatlist.innerHTML = ''; + chatlist.innerHTML = 'Nenhum contato encontrado'; + } + +}); + +getChatByNumber = async (number) => { + + const response = await fetch(`http://localhost:9090/api/chat/${number}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + const data = await response.json(); + console.log('data:', data); + const messageElement = document.createElement('div'); + messageElement.className = 'message'; + + if (data?.response?.data?.length > 0) { + + messages.innerHTML = ''; + + data?.response?.data?.forEach((message) => { + + console.log('message:', message); + + if (message?.type === 'image') { + let base64 = message?.base64; + let img = document.createElement('img'); + img.src = base64; + messageElement.appendChild(img); + messages.appendChild(messageElement); + } + + if (message?.type === 'text' || message?.type === 'chat') { + messageElement.textContent = message?.mensagem + + if(message?.author == '.'){ + + content_html = `
    +

    ${message?.mensagem}

    +
    +
    06:15
    +
    +
    +
    ` + + }else{ + + content_html = `
    +
    +
    +
    +
    ${message?.mensagem}
    +
    +
    +
    +
    +
    +
    +
    06:49
    +
    +
    +
    +
    +
    +
    +
    ` + } + + messages.innerHTML += content_html; + } + + }); + + setTimeout(() => { + $(".chat-history").scrollTop($(".chat-history")[0].scrollHeight); + }, 1000); + + } + +} + +//getChatByNumber on click get chat by number +chatlist.addEventListener('click', async (event) => { + + $('.chat-history').scrollTop(0); + + $('.chat-history').css('background-color', '#fff'); + $('.chat-history').css('background-size', 'auto'); + $('.chat-history').css('background-image', 'none'); + $('.chat-history').css('opacity', '1'); + + $('.chat-header').css('display', 'block'); + $('.chat-message').css('display', 'block'); + + messages.innerHTML = ''; + + document.querySelectorAll('.getChatByNumber').forEach((item) => { + item.classList.remove('active'); + }); + + const urlParams = new URLSearchParams(window.location.search); + const contact = urlParams?.get('contact')?.split(' ')?.join('-')?.toLowerCase()?.trim()?.toLowerCase(); + contact ? document.getElementById(contact)?.classList.add('active') : null; + + console.log('contact:', contact); + await getChatByNumber(contact); + + let title = document.querySelector(`._${contact}`).title; + document.getElementById("author").innerHTML = title; + + //copy src chat-profile-picto + let src_avatar = $(`.chat-profile-picture_${contact}`).attr("src"); + //display block + $(".chat-profile-pic").css("display", "block"); + $(".chat-profile-pic").attr("src", src_avatar); + + //lastMessage + document.querySelector(`#lastMessage`).innerHTML = `${document.querySelector(`._${contact}`).title}`; + + event.preventDefault(); + +}); + +//press enter submit +messageInput.addEventListener("keyup", function(event) { + if (event.key === "Enter") { + event.preventDefault(); + + const message = messageInput.value; + socket.emit('chat-send', message); + messageInput.value = ''; + + } + $('.scrollable-chat-panel').perfectScrollbar('update'); +}); + +socket.on('chat-send', (message) => { + + console.log('chat-send', message); + let content_html = `
    +

    ${message}

    +
    +
    06:15
    +
    +
    +
    ` + + messages.innerHTML += content_html; + + $('.scrollable-chat-panel').perfectScrollbar('update'); + +}); + +socket.on('chat-received', (message) => { + + const messageElement = document.createElement('div'); + messageElement.className = 'message'; + + if (message?.data?.type === 'image') { + let base64 = message?.data?.base64; + let img = document.createElement('img'); + img.src = base64; + img.style.width = '100px'; + img.style.height = '100px'; + messageElement.appendChild(img); + + messages.appendChild(messageElement); + + $('.scrollable-chat-panel').perfectScrollbar('update'); + } + + if (message?.data?.type === 'text') { + messageElement.textContent = message?.data?.content + + let content_html = `
    +
    +
    +
    +
    ${message?.data?.content}
    +
    +
    +
    +
    +
    +
    +
    06:49
    +
    +
    +
    +
    +
    +
    +
    ` + + messages.innerHTML += content_html; + $('.scrollable-chat-panel').perfectScrollbar('update'); + + } + + if (message?.data?.type === 'ppt' || message?.data?.type === 'audio') { + let base64 = message?.data?.base64; + let audio = document.createElement('audio'); + audio.src = base64; + audio.controls = true; + messageElement.appendChild(audio); + messages.appendChild(messageElement); + + $('.scrollable-chat-panel').perfectScrollbar('update'); + + } + + if (message?.data?.type === 'video') { + let base64 = message?.data?.base64; + let video = document.createElement('video'); + video.src = base64; + video.controls = true; + messageElement.appendChild(video); + messages.appendChild(messageElement); + + $('.scrollable-chat-panel').perfectScrollbar('update'); + + } + + //sticker + if (message?.data?.type === 'sticker') { + let base64 = message?.data?.base64; + let img = document.createElement('img'); + img.src = base64; + + img.style.width = '100px'; + img.style.height = '100px'; + + let content_html = `
    +
    +
    +
    +
    ${img.outerHTML}
    +
    +
    +
    +
    +
    +
    +
    06:49
    +
    +
    +
    +
    +
    +
    +
    ` + + messages.innerHTML += content_html; + $('.scrollable-chat-panel').perfectScrollbar('update'); + + } + +}); \ No newline at end of file diff --git a/public/assets/img/avatar.png b/public/assets/img/avatar.png new file mode 100644 index 0000000..3f0057e Binary files /dev/null and b/public/assets/img/avatar.png differ diff --git a/public/pages/auth/index.html b/public/pages/auth/index.html new file mode 100644 index 0000000..7becb11 --- /dev/null +++ b/public/pages/auth/index.html @@ -0,0 +1,40 @@ + + + + + + social network login - Bootdey.com + + + + + + + +
    +
    +
    +
    +

    Account Access

    +
    + +
    + + + + + + + +
    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/public/pages/chat/index.html b/public/pages/chat/index.html new file mode 100644 index 0000000..d098a8b --- /dev/null +++ b/public/pages/chat/index.html @@ -0,0 +1,235 @@ + + + + + + WhatsUp Messenger - Bootdey.com + + + + + + + + + + Messenger + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + +

    Profile

    +
    +
    +
    +
    +
    +
    + + + +
    + +
    +
    +
    + +
    + +
    +
    + +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    + +
    + +

    +
    +

    +
    +
    + +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..77c79af --- /dev/null +++ b/server.js @@ -0,0 +1,286 @@ +const socket = require('socket.io'); +const express = require('express'); +const axios = require('axios'); +const jwt = require('jsonwebtoken'); +const dotenv = require('dotenv'); +const app = express(); +const bodyParser = require('body-parser'); + +//middleware required +const JWTCheck = require('./middleware/JWTCheck'); + +dotenv.config(); +const env = process.env; + +const PORT = env.PORT || 3000; +const JWT_SECRET = env.JWT_SECRET || ''; +const JWT_ALGORITHMS = ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512']; + +const server = app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); + +const io = socket(server); + +app.use(express.static('public')); +app.use(bodyParser.json({ limit: '50mb' })); + +io.use((socket, next) => { + if (socket.handshake.query && socket.handshake.query.bearer) { + + const JWT_TOKEN = socket.handshake.query.bearer; + + if (!JWT_SECRET) { + console.error('Erro de configuração: JWT_SECRET não fornecido'); + return next(new Error('Erro de configuração')); + } + + jwt.verify(JWT_TOKEN, JWT_SECRET, { algorithms: JWT_ALGORITHMS }, (err, decoded) => { + + if (err) { + console.error('Erro de autenticação: token de autenticação inválido'); + return next(new Error('Token de autenticação inválido')); + } + + // console.log('Decoded:', decoded); + socket.decoded = decoded; + next(); + + }); + + } else { + console.error('Erro de autenticação: token de autenticação não fornecido'); + next(new Error('Token de autenticação não fornecido')); + } +}); + +io.on('connection', (socket) => { + + let defaultChannel = socket.handshake?.query?.channelName; + + socket.join(defaultChannel); + socket.emit('chat', 'Bem-vindo ao chat'); + // socket.broadcast.emit('chat', 'Um novo usuário entrou no chat'); + console.log('canal-sessao', defaultChannel); + + // Inscreve o socket em um canal + socket.on('subscribe', function(channel) { + socket.leave(defaultChannel); + socket.join(channel); + console.log(`Usuário ${socket.id} inscrito em: ${channel}`); + }); + + socket.on('chat-received', (message) => { + console.log('Mensagem recebida:', message); + socket.emit('chat-received', message); + }); + + socket.on('chat-send', (message) => { + //socket.broadcast.emit('message', message); + console.log('Mensagem enviada:', message); + + socket.emit('chat-send', message); + + let data = JSON.stringify({ + "number": defaultChannel, + "text": `${message}`, + }); + + let config = { + method: 'POST', + maxBodyLength: Infinity, + url: `${env.APP_API}/v1/whatsapp/sendText`, + headers: { + "Content-Type": "application/json", + "SecretKey": env.SecretKey, + "PublicToken": env.PublicToken, + "DeviceToken": env.DeviceToken, + "Authorization": `Bearer ${env.Authorization}` + }, + data : data + }; + + axios(config) + .then(function (response) { + console.log(JSON.stringify(response.data)); + }) + .catch(function (error) { + console.log(error); + }); + + + }); + + socket.on('typing', (data) => { + console.log('Usuário digitando:', data); + socket.broadcast.emit('typing', data); + }); + + socket.on('disconnect', () => { + socket.broadcast.emit('chat', 'Um usuário saiu do chat'); + }); + +}); + +io.on('error', (err) => { + console.error('Erro no socket:', err); +}); + +app.get('/', (req, res) => { + res.redirect('/chat'); +}); + +app.get('/chat', JWTCheck, (req, res) => { + res.sendFile(__dirname + '/public/pages/chat/index.html'); +}); + +app.get('/auth', (req, res) => { + res.sendFile(__dirname + '/public/pages/auth/index.html'); +}); + +app.post('/api/auth', (req, res) => { + + const { email, password } = req.body; + + console.log('email', email); + + let config = { + method: 'POST', + maxBodyLength: Infinity, + url: `${env.APP_API}/v1/login`, + headers: { + 'Content-Type': 'application/json' + }, + data : JSON.stringify({ + "email": email, + "password": password + }) + }; + + axios(config) + .then(function (response) { + res.cookie('token', response?.data?.authorization?.token, { maxAge: response?.data?.authorization?.expires_in * 1000, httpOnly: true }); + res.send(response.data); + }) + .catch(function (error) { + console.log(error); + + res.send(JSON.stringify({ + "error": true, + "message": error?.response?.data?.message + }), 401); + + }); + +}); + +app.get('/api/profile/:number', JWTCheck, (req, res) => { + + console.log('number', req.params.number); + + let config = { + method: 'POST', + maxBodyLength: Infinity, + url: `${env.APP_API}/v1/whatsapp/getHostDevice`, + headers: { + "Content-Type": "application/json", + "SecretKey": env.SecretKey, + "PublicToken": env.PublicToken, + "DeviceToken": env.DeviceToken, + "Authorization": `Bearer ${env.Authorization}` + }, + data : JSON.stringify({ + "number": req.params.number, + }) + }; + + console.log('number', req.params.number); + + axios(config) + .then(function (response) { + + console.log(JSON.stringify(response.data)); + + res.send(response.data); + + }) + .catch(function (error) { + + console.log(error?.response?.data?.message); + + + res.send(error); + }); + +}); + +app.get('/api/chat/:defaultChannel', JWTCheck, (req, res) => { + + let config = { + method: 'POST', + maxBodyLength: Infinity, + url: `${env.APP_API}/v1/whatsapp/getMessagesChat`, + headers: { + "Content-Type": "application/json", + "SecretKey": env.SecretKey, + "PublicToken": env.PublicToken, + "DeviceToken": env.DeviceToken, + "Authorization": `Bearer ${env.Authorization}` + }, + data : JSON.stringify({ + "number": req.params.defaultChannel, + "count": 100 + }) + }; + + axios(config) + .then(function (response) { + res.send(response.data); + }) + .catch(function (error) { + res.send(error); + }); + +}); + +app.get('/api/chats/:defaultChannel', JWTCheck, (req, res) => { + + let config = { + method: 'post', + maxBodyLength: Infinity, + url: `${env.APP_API}/v1/whatsapp/getAllChats`, + headers: { + "Content-Type": "application/json", + "SecretKey": env.SecretKey, + "PublicToken": env.PublicToken, + "DeviceToken": env.DeviceToken, + "Authorization": `Bearer ${env.Authorization}` + } + }; + + axios(config) + .then(function (response) { + res.send(response.data); + }) + .catch(function (error) { + res.send(error); + }); + +}); + +app.post('/api/webhook/:defaultChannel', (req, res) => { + + let defaultChannel = req.params.defaultChannel; + const body = req.body; + + content = body?.data?.body; + + console.log('defaultChannel:', defaultChannel); + + if(body?.data?.isGroupMsg == false){ + io.to(defaultChannel).emit('chat-received', body); + } + + res.send('ok'); + +})