import {models} from '../models' import * as crypto from 'crypto' import * as socket from '../utils/socket' import * as helpers from '../helpers' import * as jsonUtils from '../utils/json' import {success, failure} from '../utils/res' import password from '../utils/password' import * as path from 'path' import { Op } from 'sequelize' const constants = require(path.join(__dirname,'../../config/constants.json')) export const getContacts = async (req, res) => { const contacts = await models.Contact.findAll({ where:{deleted:false}, raw: true }) const invites = await models.Invite.findAll({ raw: true }) const chats = await models.Chat.findAll({ where:{deleted:false}, raw: true }) const subscriptions = await models.Subscription.findAll({ raw: true }) const pendingMembers = await models.ChatMember.findAll({where:{ status: constants.chat_statuses.pending }}) const contactsResponse = contacts.map(contact => { let contactJson = jsonUtils.contactToJson(contact) let invite = invites.find(invite => invite.contactId == contact.id) if (invite) { contactJson.invite = jsonUtils.inviteToJson(invite) } return contactJson }); const subsResponse = subscriptions.map(s=> jsonUtils.subscriptionToJson(s,null)) const chatsResponse = chats.map(chat=> { const theChat = chat.dataValues||chat if(!pendingMembers) return jsonUtils.chatToJson(theChat) const membs = pendingMembers.filter(m=>m.chatId===chat.id) || [] theChat.pendingContactIds = membs.map(m=>m.contactId) return jsonUtils.chatToJson(theChat) }) success(res, { contacts: contactsResponse, chats: chatsResponse, subscriptions: subsResponse }) } export const generateToken = async (req, res) => { console.log('=> generateToken called', { body: req.body, params: req.params, query: req.query }) const owner = await models.Contact.findOne({ where: { isOwner: true, authToken: null }}) const pwd = password if(process.env.USE_PASSWORD==='true'){ if(pwd!==req.query.pwd) { failure(res, 'Wrong Password') return } else { console.log("PASSWORD ACCEPTED!") } } if (owner) { const hash = crypto.createHash('sha256').update(req.body['token']).digest('base64'); console.log("req.params['token']", req.params['token']); console.log("hash", hash); owner.update({ authToken: hash }) success(res,{}) } else { failure(res,{}) } } export const updateContact = async (req, res) => { console.log('=> updateContact called', { body: req.body, params: req.params, query: req.query }) let attrs = extractAttrs(req.body) const contact = await models.Contact.findOne({ where: { id: req.params.id }}) if(!contact) { return failure(res, 'no contact found') } // update self const owner = await contact.update(jsonUtils.jsonToContact(attrs)) success(res, jsonUtils.contactToJson(owner)) if (!contact.isOwner) return if (!(attrs['contact_key'] || attrs['alias'] || attrs['photo_url'])) { return // skip if not at least one of these } // send updated owner info to others! const contactIds = await models.Contact.findAll({where:{deleted:false}}) .filter(c=> c.id!==1 && c.publicKey).map(c=> c.id) if (contactIds.length == 0) return console.log("=> send contact_key to", contactIds) helpers.sendContactKeys({ contactIds: contactIds, sender: owner, type: constants.message_types.contact_key, }) } export const exchangeKeys = async (req, res) => { console.log('=> exchangeKeys called', { body: req.body, params: req.params, query: req.query }) const contact = await models.Contact.findOne({ where: { id: req.params.id }}) const owner = await models.Contact.findOne({ where: { isOwner: true }}) success(res, jsonUtils.contactToJson(contact)) helpers.sendContactKeys({ contactIds: [contact.id], sender: owner, type: constants.message_types.contact_key, }) } export const createContact = async (req, res) => { console.log('=> createContact called', { body: req.body, params: req.params, query: req.query }) let attrs = extractAttrs(req.body) const owner = await models.Contact.findOne({ where: { isOwner: true }}) const existing = attrs['public_key'] && await models.Contact.findOne({where:{publicKey:attrs['public_key']}}) if(existing) { const updateObj:{[k:string]:any} = {fromGroup:false} if(attrs['alias']) updateObj.alias = attrs['alias'] await existing.update(updateObj) return success(res, jsonUtils.contactToJson(existing)) } if(attrs['public_key'].length>66) attrs['public_key'] = attrs['public_key'].substring(0,66) const createdContact = await models.Contact.create(attrs) const contact = await createdContact.update(jsonUtils.jsonToContact(attrs)) success(res, jsonUtils.contactToJson(contact)) helpers.sendContactKeys({ contactIds: [contact.id], sender: owner, type: constants.message_types.contact_key, }) } export const deleteContact = async (req, res) => { const id = parseInt(req.params.id||'0') if(!id || id===1) { failure(res, 'Cannot delete self') return } const contact = await models.Contact.findOne({ where: { id } }) if(!contact) return const owner = await models.Contact.findOne({ where: { isOwner: true }}) const tribesImAdminOf = await models.Chat.findAll({where:{ownerPubkey:owner.publicKey}}) const tribesIdArray = tribesImAdminOf && tribesImAdminOf.length && tribesImAdminOf.map(t=>t.id) let okToDelete = true if(tribesIdArray && tribesIdArray.length) { const thisContactMembers = await models.ChatMember.findAll({where:{contactId:id,chatId:{[Op.in]:tribesIdArray}}}) if(thisContactMembers&&thisContactMembers.length){ // IS A MEMBER! dont delete, instead just set from_group=true okToDelete=false await contact.update({fromGroup:true}) } } if(okToDelete){ await contact.update({ deleted:true, publicKey:'', photoUrl:'', alias:'Unknown', contactKey:'', }) } // find and destroy chat & messages const chats = await models.Chat.findAll({where:{deleted:false}}) chats.map(async chat => { if (chat.type === constants.chat_types.conversation) { const contactIds = JSON.parse(chat.contactIds) if (contactIds.includes(id)) { await chat.update({ deleted: true, uuid:'', contactIds:'[]', name:'' }) await models.Message.destroy({ where: { chatId: chat.id } }) } } }) await models.Invite.destroy({ where: { contactId: id } }) await models.Subscription.destroy({ where: { contactId: id } }) success(res, {}) } export const receiveContactKey = async (payload) => { console.log('=> received contact key', JSON.stringify(payload)) const dat = payload.content || payload const sender_pub_key = dat.sender.pub_key const sender_contact_key = dat.sender.contact_key const sender_alias = dat.sender.alias || 'Unknown' const sender_photo_url = dat.sender.photo_url if(!sender_pub_key) { return console.log("no pubkey!") } const owner = await models.Contact.findOne({ where: { isOwner: true }}) const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key, status: constants.contact_statuses.confirmed }}) if (sender_contact_key && sender) { const objToUpdate:{[k:string]:any} = {contactKey: sender_contact_key} if(sender_alias) objToUpdate.alias = sender_alias if(sender_photo_url) objToUpdate.photoUrl = sender_photo_url await sender.update(objToUpdate) socket.sendJson({ type: 'contact', response: jsonUtils.contactToJson(sender) }) } else { console.log("DID NOT FIND SENDER") } helpers.sendContactKeys({ contactPubKey: sender_pub_key, sender: owner, type: constants.message_types.contact_key_confirmation, }) } export const receiveConfirmContactKey = async (payload) => { console.log(`=> confirm contact key for ${payload.sender&&payload.sender.pub_key}`, JSON.stringify(payload)) const dat = payload.content || payload const sender_pub_key = dat.sender.pub_key const sender_contact_key = dat.sender.contact_key const sender_alias = dat.sender.alias || 'Unknown' const sender_photo_url = dat.sender.photo_url if(!sender_pub_key) { return console.log("no pubkey!") } const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key, status: constants.contact_statuses.confirmed }}) if (sender_contact_key && sender) { const objToUpdate:{[k:string]:any} = {contactKey: sender_contact_key} if(sender_alias) objToUpdate.alias = sender_alias if(sender_photo_url) objToUpdate.photoUrl = sender_photo_url await sender.update(objToUpdate) socket.sendJson({ type: 'contact', response: jsonUtils.contactToJson(sender) }) } } const extractAttrs = body => { let fields_to_update = ["public_key", "node_alias", "alias", "photo_url", "device_id", "status", "contact_key", "from_group", "private_photo", "notification_sound"] let attrs = {} Object.keys(body).forEach(key => { if (fields_to_update.includes(key)) { attrs[key] = body[key] } }) return attrs }