diff --git a/css/oi.css b/css/oi.css index 7736ddb47a..d0a7240a25 100644 --- a/css/oi.css +++ b/css/oi.css @@ -156,3 +156,7 @@ input::placeholder { z-index: 1000; outline: none; } + +.cactus-editor { + display: none!important; +} \ No newline at end of file diff --git a/db/Ideas.db.backup b/db/Ideas.db.backup index d5dadd159e..a14bde24b2 100644 Binary files a/db/Ideas.db.backup and b/db/Ideas.db.backup differ diff --git a/media/favicon.png b/media/favicon.png new file mode 100644 index 0000000000..eaec21b5de Binary files /dev/null and b/media/favicon.png differ diff --git a/myServer.js b/myServer.js index f4d5859fb7..5a6772e867 100644 --- a/myServer.js +++ b/myServer.js @@ -7,28 +7,26 @@ const express = require("express"); const fs = require("fs"); const sqlite3 = require("sqlite3").verbose(); const https = require("https"); -var path = require("path"); -var bcrypt = require("bcrypt"); -const sdk = require("matrix-js-sdk"); -var request = require('request'); +const path = require("path"); +const bcrypt = require("bcrypt"); +const request = require("request"); const passport = require("passport"); const flash = require("express-flash"); const session = require("express-session"); -const methodOverride = require('method-override'); +const methodOverride = require("method-override"); +const matrix = require("./src/oi/matrix"); +// const db = require("./src/oi/sqliteDb"); const app = express(); const http = express(); const port = process.argv[2] || 80; const portSSL = process.argv[3] || 443; -//matrix login -const client = sdk.createClient("https://matrix.org"); -client.login("m.login.password", {"user": "openidea", "password": process.env.MATRIX_PASSWORD}).then((response) => { - console.log(response.access_token); -}); +matrix.loginOpenIdea(); //usermanagement using passport const initializePassport = require("./passport-config"); +// initializePassport(passport, async db.getUserByEmail); initializePassport(passport, async function getUserByEmail(email) { return new Promise((resolve) => { let sql = `SELECT name, email, pwHash password FROM User WHERE email is ?`; @@ -41,8 +39,7 @@ initializePassport(passport, async function getUserByEmail(email) { if (err) throw err; resolve(rows); }); - } - else resolve(rows); + } else resolve(rows); }); }); }); @@ -91,15 +88,14 @@ app.use((req, res, next) => { app.use(passport.initialize()); app.use(passport.session()); -app.use(methodOverride('_method')); +app.use(methodOverride("_method")); //app start page app.get("/", checkAuthenticated, function (req, res) { - if(req.session.isNew) { - if(req.query.Idea) res.redirect('/?user=new&Idea='+req.query.Idea); - else res.redirect('/?user=new'); - } - else { + if (req.session.isNew) { + if (req.query.Idea) res.redirect("/?user=new&Idea=" + req.query.Idea); + else res.redirect("/?user=new"); + } else { try { console.log(req.user.name + " visited us"); } catch (error) { @@ -109,7 +105,6 @@ app.get("/", checkAuthenticated, function (req, res) { } }); - //respond with topics app.get("/getTopics", function (req, res) { //read all topics from Database @@ -135,10 +130,10 @@ app.get("/getSkills", function (req, res) { }); //respond to getIdeas GET request -app.get("/getIdeas", checkAuthenticated, function(req, res) { +app.get("/getIdeas", checkAuthenticated, function (req, res) { //read all places from Database var idearows; - db.serialize(function() { + db.serialize(function () { let sql = `SELECT lon, lat, IdeaID, upvotes, downvotes FROM v_PlacesVotes ORDER BY lat`; db.all(sql, [], async (err, rows) => { if (err) throw err; @@ -148,16 +143,16 @@ app.get("/getIdeas", checkAuthenticated, function(req, res) { idearows[key].tags = tmp.tags; idearows[key].skills = tmp.skills; idearows[key].user = tmp.user; - }; + } res.json(idearows); }); }); }); function addTagsSkillsUsers(id) { - var idearows = {tags: [], skills: [], user: []}; + var idearows = { tags: [], skills: [], user: [] }; return new Promise((resolve) => { - db.serialize(function() { + db.serialize(function () { let sql = `SELECT Tag FROM Idea_Tags WHERE Idea is ?`; db.all(sql, [id], (err, rows) => { if (err) throw err; @@ -188,17 +183,17 @@ function addTagsSkillsUsers(id) { }); }); }); -}; +} //get svgs -app.get("/svgTest", function(req,res) { +app.get("/svgTest", function (req, res) { let _id = req.query.IdeaID; let _title = ""; db.serialize(function () { let sql = `SELECT ID, title FROM Idea WHERE ID is ?`; db.get(sql, [_id], (err, row) => { if (err) throw err; - if(row) _title = row.title; + if (row) _title = row.title; else _title = "title not found"; // console.log("test query " + _id + " title " + _title); var testSVG = ` @@ -220,12 +215,21 @@ app.get("/svgTest", function(req,res) { ${_title} `; - res.type('svg'); + res.type("svg"); res.send(testSVG); }); }); }); +//endpoint for new comments +app.post("/submitComment", checkAuthenticated, async function (req, res, next) { + if(res._headers.login !== "false") + console.log(await matrix.sendMessage(res._headers.login, req.body.IdeaID, req.body.comment, db)); + //elso only if we want to let people comment without login in! + //else console.log(await matrix.sendMessage('guest', req.body.IdeaID, req.body.comment, db)); + res.json({msg: "comment saved"}); +}); + //respond to getIdea POST request app.post("/getIdea", checkAuthenticated, function (req, res) { if (!req.body.IdeaID) res.json(); @@ -294,7 +298,7 @@ app.post("/submitVote", checkAuthenticatedCancel, function (req, res) { 1: req.body.IdeaID, 2: req.body.upvote, 3: datenow, - 4: req.user.name + 4: req.user.name, }, function (err) { if (err) return console.log(err.message); @@ -330,7 +334,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { ); var tags = req.body.tags.split(","); tags.forEach(function (item, index) { - if(item.trim() != ""){ + if (item.trim() != "") { db.run( "INSERT OR IGNORE INTO Tags(Name) VALUES(?1)", { @@ -354,7 +358,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { }); var skills = req.body.skills.split(","); skills.forEach(function (item, index) { - if(item.trim() != ""){ + if (item.trim() != "") { db.run( "INSERT OR IGNORE INTO Skills(Name) VALUES(?1)", { @@ -381,7 +385,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { { 1: lastID, 2: req.user.name, - 3: 0 + 3: 0, }, function (err) { if (err) return console.log(err.message); @@ -391,17 +395,17 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { /*****************************| | mastodon part start | |*****************************/ - if(req.body.mastodon == "true"){ + if (req.body.mastodon == "true") { console.log("publish on mastodon: " + req.body.mastodon); var tagsBody = ""; - tags.some(function(item, index) { - if(item.trim() != "") { - tagsBody += ("#" + item.trim() + " "); + tags.some(function (item, index) { + if (item.trim() != "") { + tagsBody += "#" + item.trim() + " "; console.log("item: " + index + "tag: " + "#" + item.trim() + " "); } //if 3 tags written stop function - if(index >= 2) return true; + if (index >= 2) return true; else return false; }); @@ -411,25 +415,26 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { + "\n" + "https://openidea.io/?Idea=" + lastID + "\n" + tagsBody; - var jsonBody = {'status': truncate(statusText, 425), - 'visibility': 'unlisted'}// public, unlisted, private, direct - ; + var jsonBody = { + status: truncate(statusText, 425), + visibility: "unlisted", + }; // public, unlisted, private, direct var clientServerOptions = { - url: 'https://botsin.space/api/v1/statuses', + url: "https://botsin.space/api/v1/statuses", body: JSON.stringify(jsonBody), - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', - 'Authorization' : 'Bearer ' + process.env.MASTODON_ACCESS_TOKEN - } + "Content-Type": "application/json", + Authorization: "Bearer " + process.env.MASTODON_ACCESS_TOKEN, + }, }; - request(clientServerOptions, function(error, response, body){ + request(clientServerOptions, function (error, response, body) { // console.log(body); }); - function truncate(str, n){ - return (str.length > n) ? str.substr(0, n-3) + '...' : str; - }; + function truncate(str, n) { + return str.length > n ? str.substr(0, n - 3) + "..." : str; + } } /*****************************| | mastodon part end | @@ -440,27 +445,9 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { }); }); - //support request to matrix room app.post("/submitSupportRequest", function (req, res, next) { - client.startClient(); - - var testRoomId = "!HTcpkpXWLaaunauYsD:cactus.chat"; - - var content = { - "body": "#" + req.body.IdeaID + "\n" + req.body.text + "\n\nsend from: ", - "msgtype": "m.text" - }; - if(req.user){ - content.body += req.user.email; - } - else {content.body += "anonymous";} - - client.sendEvent(testRoomId, "m.room.message", content, "").then((res) => { - // message sent successfully - }).catch((err) => { - console.log(err); - }); + matrix.sendSupportRequest(req); res.send("Message send to support"); }); @@ -469,23 +456,23 @@ app.post("/submitSupportRequest", function (req, res, next) { app.post("/submitLogin", function (req, res, next) { passport.authenticate("local", (err, user, info) => { //if authenticate failed respond with info - if(!user) return res.json(info); + if (!user) return res.json(info); else { //start user session - req.logIn(user, function(err) { - if (err) { return next(err); } + req.logIn(user, function (err) { + if (err) { + return next(err); + } return res.json(user.name); }); } - })(req,res,next) - } -); + })(req, res, next); +}); app.delete("/SubmitLogout", function (req, res) { req.logOut(); res.json(); -} - ); +}); //handle register POST request app.post("/submitRegister", async function (req, res) { @@ -494,31 +481,41 @@ app.post("/submitRegister", async function (req, res) { res.json({ email: req.user.email, }); - } catch {} + } catch(e) {} + + //reject username if it uses not allowed character + const regex = /[^A-Za-z0-9 ]+/g; + if(regex.test(req.body.name)) { + res.json({message: "username not allowed"}); + return; + } + console.log(req.body.name); console.log(req.body.email); - console.log(req.body.password); try { const hashedPassword = await bcrypt.hash(req.body.password, 10); console.log(hashedPassword); + const matrixUser = await matrix.createUser(req.body.name); db.serialize(function () { db.run( - "INSERT INTO User(name,email,pwHash) VALUES(?1,?2,?3)", + "INSERT INTO User(name,email,pwHash,matrixname,matrixpw) VALUES(?1,?2,?3,?4,?5)", { 1: req.body.name, 2: req.body.email, 3: hashedPassword, + 4: matrixUser.matrixname, + 5: matrixUser.matrixpw, }, function (err) { if (err) { console.log(err.message); - res.json({"message": err.message}); + res.json({ message: err.message }); } res.json("success"); } ); }); - } catch { + } catch(e) { console.log("error while user registered"); } }); @@ -550,11 +547,11 @@ function checkAuthenticated(req, res, next) { function checkAuthenticatedCancel(req, res, next) { if (req.isAuthenticated()) { - res.append('login', req.user.name); + res.append("login", req.user.name); console.log("checkAuthenticated " + req.user.name); next(); } else { - res.append('login', false); + res.append("login", false); res.json("error"); } } diff --git a/package.json b/package.json index 38a9f436e9..3501fd50ae 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,16 @@ "express": "^4.17.1", "express-flash": "0.0.2", "express-session": "^1.17.1", + "generate-password": "^1.6.0", "material-design-icons": "^3.0.1", "materialize-css": "^1.0.0", "matrix-js-sdk": "^9.9.0", "method-override": "^3.0.0", + "msdf-bmfont-xml": "^2.5.4", "nodemon": "^2.0.7", "passport": "^0.4.1", "passport-local": "^1.0.0", - "sqlite3": "^5.0.2", - "msdf-bmfont-xml": "^2.5.4" + "sqlite3": "^5.0.2" }, "exports": { ".": "./src/og/index.js", diff --git a/src/oi/SideLoginRegister.js b/src/oi/SideLoginRegister.js index 494ce51a04..52ab426ece 100644 --- a/src/oi/SideLoginRegister.js +++ b/src/oi/SideLoginRegister.js @@ -128,7 +128,8 @@ function showRegister() {
- + +
@@ -141,7 +142,7 @@ function showRegister() {
-
diff --git a/src/oi/SideShowIdea.js b/src/oi/SideShowIdea.js index c9989d02f0..9955cb3f0a 100644 --- a/src/oi/SideShowIdea.js +++ b/src/oi/SideShowIdea.js @@ -17,6 +17,8 @@ function show(IdeaID) { document.getElementById("up-btn").onclick = function () {SideLoginRegister.showLogin();}; document.getElementById("down-btn").style.opacity = "0.4"; document.getElementById("down-btn").onclick = function () {SideLoginRegister.showLogin();}; + document.getElementById("commentForm").textContent = "Comments:"; + document.getElementById("commentForm").classList.add("cactus-comment"); } //cactus chat @@ -64,7 +66,23 @@ function show(IdeaID) {
+ +
+
+
+ + + + +
+
+
+
+
Join this comment section on your favorit matrix chat app!
#comments_openidea.io_Idea#${IdeaID}:cactus.chat
+
Vote this idea
@@ -88,6 +106,13 @@ function show(IdeaID) {
`; SidePanel.show(html); + //send comment when hitting enter in comment field + document.getElementById('comment').addEventListener('keydown', (event) => { + if (event.code === 'Enter') { + event.preventDefault(); + SideShowIdea.sendComment(IdeaID); + } + }); var LoginRegister = ` Login @@ -131,19 +156,41 @@ function sendSupportRequest(IdeaID){ xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { alert(this.responseText); - // var obj = JSON.parse(this.responseText); - // if(obj) { - // if(obj == "error") alert("error - are you still logged in?"); - // else { - // myCreateIdea(lon,lat,obj,0); - // SidePanel.hide(); - // } - // } + SidePanel.hide(); }}; - var _testText = document.getElementById("text").value; + var _message = document.getElementById("text").value; xhttp.open("POST", "submitSupportRequest", true); xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhttp.send("text="+_testText+"&IdeaID="+IdeaID); + xhttp.send("text="+_message+"&IdeaID="+IdeaID); } export { sendSupportRequest }; + +function sendComment(IdeaID) { + if(!document.forms.comment_form.comment.checkValidity()) { + return false; + } + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + document.forms.comment_form.comment.value = ""; + + //cactus chat - update + let comsec = document.getElementsByClassName("cactus-container"); + comsec[0].innerHTML = ""; + comsec[0].id = "comment-section"; + comsec[0].classList.remove("cactus-container"); + initComments({ + node: document.getElementById("comment-section"), + defaultHomeserverUrl: "https://matrix.cactus.chat:8448", + serverName: "cactus.chat", + siteName: "openidea.io", + commentSectionId: "Idea#" + IdeaID + }); + //cactus chat end + }}; + xhttp.open("POST", "submitComment", true); + xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhttp.send("comment="+document.getElementById("comment").value+"&IdeaID="+IdeaID); +}; +export { sendComment }; \ No newline at end of file diff --git a/src/oi/matrix-wo-cactus.js b/src/oi/matrix-wo-cactus.js new file mode 100644 index 0000000000..a418d8657f --- /dev/null +++ b/src/oi/matrix-wo-cactus.js @@ -0,0 +1,157 @@ + +/** Function not in use - this is done by cactus comments + * function to create comment rooms for ideas + * @param {number} IdeaID - IdeaID for which a room should be created + * @param {string} [forCreation] - Display name of room, if not provided room will be namend 'comments for Idea #IdeaID' + * @returns {Room} - room object + */ +function createRoom(IdeaID, roomName){ + if(!openideaClient) loginOpenIdea(); + roomName = roomName || 'comments for Idea #' + IdeaID; + var options = { + name: roomName, + room_alias_name: RoomAlias(IdeaID, true), + visibility: "public" + }; + + console.log(options); + + return openideaClient.createRoom(options); +} + + +//TODO +/** + * function to fetch (num) last messages of room + * @param {number} IdeaID - IdeaID to fetch messages from + * @param {number} num - number of fetched messages + * @returns {(string|string[])} - returns num messages of matrix room asocciated with IdeaID + */ +async function getLastMessages(IdeaID, num){ + if(!openideaClient) loginOpenIdea(); + + await openideaClient.startClient({initialSyncLimit: 10}); +// await openideaClient.once('sync', function(state, prevState, res) { +// if(state === 'PREPARED') { +// console.log("prepared"); +// } else { +// console.log(state); +// process.exit(1); +// } +// }); + +var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); +// try{ +// var room = await openideaClient.getRoom(roomID.roomId); +// } catch (e) {console.log(e);} + +var iTest = 0; + +await openideaClient.on("Room.timeline", function(event, room, toStartOfTimeline) { + if (event.getType() !== "m.room.message") { + return; // only use messages + } + //if(room.name == RoomAlias(IdeaID)) console.log(room); + if(room.roomId == roomID.roomId) console.log("finaly"); + console.log(iTest++); + console.log(room.roomId); + console.log(event.event.content.body); +}); + +// var scrollback = await openideaClient.scrollback(roomID); +// Object.keys(openideaClient.store.rooms).forEach((roomId) => { +// client.getRoom(roomId).timeline.forEach(t => { +// console.log(t.event); +// }); +// }); + + let messages; + //join room (even if joined already returns the roomID which is needed to write to it) + // var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); + // try{ + // openideaClient.getRoom(roomID.roomId).timeline.forEach(t => { + // console.log(t.event); + // }); + + // // messages = openideaClient.scrollback(roomID.roomId, num); + // // console.log(messages); + // } catch (e) {console.log(e);} + + return messages; +} + +/** + * function to create consistant roomAliases + * @param {number} IdeaID - IdeaID which needs a room + * @param {boolean} [forCreation=false] - if true switches to only local part as return (like Idea_IdeaID) + * @returns {string} - roomAlias looks like this: #Idea_IdeaID:dohm.work + */ +function RoomAlias(IdeaID, forCreation=false){ + var roomAlias = 'Idea_' + IdeaID; + if(!forCreation) roomAlias = '#' + roomAlias + ':' + matrixGroupUrl; + return roomAlias; +} + +/** + * function to send usermessage to idea room + * @param {string} user - user which sends the message + * @param {number} IdeaID - number of idea hosting the room + * @param {string} message - usermessage + * @param {*} db - opened sqlite database to fetch users matrix data from + * @returns {string} - errormessage or undefined at success + */ +async function sendMessage(user, IdeaID, message, db) { + //get matrixname and matrixpw from database (db) + let matrix = await getMatrixUserByName(user, db); + console.log(matrix); + if(matrix == null || !matrix.matrixname || !matrix.matrixpw) return 'username not found'; + //matrix login + const client = sdk.createClient(matrixServer); + await client + .login("m.login.password", { + user: matrix.matrixname, + password: matrix.matrixpw, + }) + .then((response) => { + // console.log(response.access_token); + }); + await client.startClient(); + + //join room (even if joined already returns the roomID which is needed to write to it) + var roomID + try { + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } catch (e) { + if(e.errcode == 'M_NOT_FOUND') + { + try { + await createRoom(IdeaID); + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } + catch (e) {return "room coudn't be joined or created"} + } + } + try { + //create content for message to be send + var content = { + body: message, + msgtype: "m.text", + }; + //matrix send message to room + client + .sendEvent(roomID, "m.room.message", content, "") + .then((res) => { + // message sent successfully + return; + }) + .catch((err) => { + console.log(err); + return err; + }); + } + catch (e) { + return e; + } +}; \ No newline at end of file diff --git a/src/oi/matrix.js b/src/oi/matrix.js new file mode 100644 index 0000000000..51f0094a21 --- /dev/null +++ b/src/oi/matrix.js @@ -0,0 +1,267 @@ +/** + * @fileOverview + * @name matrix.js + * @author Jannis Dohm + * @license MIT + */ +const https = require("https"); +const generator = require("generate-password"); +const crypto = require("crypto"); +const sdk = require("matrix-js-sdk"); +const { exitCode } = require("process"); +const db = require("./sqliteDb"); +const { RoomState } = require("matrix-js-sdk"); +const { resolve } = require("path"); +const { promiseMapSeries } = require("matrix-js-sdk/lib/utils"); + +/* url to the matrix server for crating new users + often uses matrix.example.com + */ +const matrixServer = "matrix.dohm.work"; +/* url to append on matrix rooms + often uses example.com + */ +const matrixGroupUrl = "cactus.chat"; + +let urldata = { + host: matrixServer, + path: "/_synapse/admin/v1/register", + method: "GET", +}; +let urlRegData = { + nonce: "", + username: "", + displayname: "", + password: "", + mac: "", +}; +let matrix = { + matrixname: "", + matrixpw: "", +}; + +let openideaClient; + +/** + * This function creates a matrix account for a given user. + * The username will be transformed to all lowercase and spaces will be replaced with underscores + * for more informations visit: https://github.com/matrix-org/synapse/blob/master/docs/admin_api/register_api.rst + * @param {string} username - wanted username of matrix user + * @returns {object} - matrix object + * @property {string} matrixname - the given matrix username + * @property {string} matrixpw - the automatically created matrix pw + */ +async function createUser(username) { + return new Promise((resolve) => { + //generate lowercase only charakters and underscores matrix name! + //make username lowercase + matrix.matrixname = username.toLowerCase(); + //replace spaces with underscores + matrix.matrixname = matrix.matrixname.replace(/\s+/g, "_"); + //delete all non alphanumeric and not underscore (shoudn't be necessary since they are not allowed as usernames anyway) + matrix.matrixname = matrix.matrixname.replace(/\W/g, ""); + urlRegData.username = matrix.matrixname; + urlRegData.displayname = username; + var password = generator.generate({ + length: 24, + numbers: true, + }); + urlRegData.password = password; + matrix.matrixpw = password; + //for debugging and not loosing users in the beginning + console.log("matrixname: \t" + matrix.matrixname); + console.log("pw: \t" + password); + + function OnResponse(response) { + console.log(`statusCode 1: ${response.statusCode}`); + var data = ""; //This will store the page we're downloading. + response.on("data", function (chunk) { + //Executed whenever a chunk is received. + data += chunk; //Append each chunk to the data variable. + }); + + response.on("end", function () { + urlRegData.nonce = JSON.parse(data).nonce; + urlRegData.mac = generate_mac( + urlRegData.nonce, + urlRegData.username, + urlRegData.password + ); + + let urlPostData = { + host: "matrix.dohm.work", + path: "/_synapse/admin/v1/register", + port: 443, + method: "POST", + headers: { + "Content-Type": "application/json", + "Content-Length": JSON.stringify(urlRegData).length, + }, + }; + + let postreq = https.request(urlPostData, (res) => { + console.log(`statusCode: ${res.statusCode}`); + console.log("res: " + res.statusMessage); + + resolve( matrix ); + + // res.on("data", (d) => { + // console.log(d); + // }); + }); + + console.log(urlRegData); + postreq.write(JSON.stringify(urlRegData)); + postreq.end(); + }); + } + + console.log(urldata); + https.request(urldata, OnResponse).end(); +}); +} + +/** + * function to generate mac for user creation via shared secret + * @param {number} nonce - nonce which is a one time key to generate the mac. Can be requested from the matrix-synapse server. + * @param {string} user - user to be created + * @param {strin} password - password of user + * @param {boolean} [somebody=false] - wether or not the user should get admin rights + * @returns {number} - mac in HEX + */ +function generate_mac(nonce, user, password, admin = false) { + var mac = crypto.createHmac("sha1", process.env.DOHMWORK_MATRIX_SS); + + mac.update(nonce.toString()); + mac.update("\x00"); + mac.update(user.toString()); + mac.update("\x00"); + mac.update(password.toString()); + mac.update("\x00"); + if (admin) mac.update("admin"); + else mac.update("notadmin"); + return mac.digest("hex"); +} + +/** + * function to send usermessage to idea room + * @param {string} user - user which sends the message + * @param {number} IdeaID - number of idea hosting the room + * @param {string} message - usermessage + * @param {*} dbOpened - opened sqlite database to fetch users matrix data from + * @returns {string} - errormessage or undefined at success + */ +async function sendMessage(user, IdeaID, message, dbOpened) { + //get matrixname and matrixpw from database (db) + let matrix = await db.getMatrixUserByName(user, dbOpened); + console.log(matrix); + if (matrix == null || !matrix.matrixname || !matrix.matrixpw) + return "username not found"; + //matrix login + const client = sdk.createClient("https://" + matrixServer); + await client + .login("m.login.password", { + user: matrix.matrixname, + password: matrix.matrixpw, + }) + .then((response) => { + // console.log(response.access_token); + }); + await client.startClient(); + + //join room (even if joined already returns the roomID which is needed to write to it) + var roomID; + try { + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } catch (e) { + console.log(e); + } + try { + //create content for message to be send + var content = { + body: message, + msgtype: "m.text", + }; + //matrix send message to room + client + .sendEvent(roomID, "m.room.message", content, "") + .then((res) => { + // message sent successfully + return; + }) + .catch((err) => { + console.log(err); + return err; + }); + } catch (e) { + return e; + } +} + +/** + * function to create consistant roomAliases + * @param {number} IdeaID - IdeaID which needs a room + * @returns {string} - roomAlias looks like this: #comments_openidea.io_Idea#IdeaId:cactus.chat + */ +function RoomAlias(IdeaID) { + var roomAlias = "#comments_openidea.io_Idea#" + IdeaID + ":" + matrixGroupUrl; + return roomAlias; +} + +/** + * function to get link to comment room of given IdeaID + * @param {number} IdeaID - IdearID of idea of which the url is requested + * @returns {string} - url to join room looks like this: https://matrix.to/#/#Idea_IdeaID:dohm.work + */ +function getJoinUrl(IdeaID) { + let url = "https://matrix.to/#/" + RoomAlias(IdeaID); + return url; +} + +/** + * function to call on init. + * This function handels the login of the openidea user which is used for reports etc. + */ +function loginOpenIdea() { + //return if openideaClint was already set + if (openideaClient) return; + openideaClient = sdk.createClient("https://" + matrixServer); + openideaClient + .login("m.login.password", { + user: process.env.MATRIX_DW_USER, + password: process.env.MATRIX_DW_PASSWORD, + }) + .then((response) => { + console.log(response.access_token); + }); +} + +/** + * function to send support message to support chat + * @param {Object} req - req object, this functions reads the body.IdeaID, body.text, req.user + */ +function sendSupportRequest(req) { + if(!openideaClient) loginOpenIdea(); + openideaClient.startClient(); + + var RoomId = "!HTcpkpXWLaaunauYsD:cactus.chat"; + + var content = { + body: "#" + req.body.IdeaID + "\n" + req.body.text + "\n\nsend from: ", + msgtype: "m.text", + }; + if (req.user) { + content.body += req.user.email; + } else { + content.body += "anonymous"; + } + + openideaClient + .sendEvent(RoomId, "m.room.message", content, "") + .catch((err) => { + console.log(err); + }); +} + +module.exports = { createUser, sendMessage, getJoinUrl, loginOpenIdea, sendSupportRequest}; \ No newline at end of file diff --git a/src/oi/sqliteDb.js b/src/oi/sqliteDb.js new file mode 100644 index 0000000000..22b15064f0 --- /dev/null +++ b/src/oi/sqliteDb.js @@ -0,0 +1,70 @@ +/** + * @fileOverview + * @name sqliteDb.js + * @author Jannis Dohm + * @license MIT + */ + +const sqlite3 = require("sqlite3").verbose(); + + +// Start sqlite3 database connection +function startDb(){ +let db = new sqlite3.Database("./db/Ideas.db", (err) => { + if (err) { + return console.error(err.message); + } + console.log("Connected to the SQlite database."); + return db; +}); +} + + + + +/** + * function to get matrixname and matrixpw of given user + * @param {string} username - username to fetch matrix data for + * @param {*} db - opened sqlite database to fetch data from + * @returns {Object} - matrix user data + * @property {string} matrixname - matrix username + * @property {string} matrixpw - matrix password + */ +async function getMatrixUserByName(username, db) { + return new Promise((resolve) => { + let sql = `SELECT name, matrixname, matrixpw FROM User WHERE name is ?`; + db.get(sql, [username], (err, rows) => { + if (err) throw err; + console.log(rows); + resolve(rows); + }); + }); +} + +/** + * function to get user object by email or username + * @param {string} email - email of searched user + * @param {boolean} [alsoCheckUserName=true] - if set to true (default) this function will check if the given email was a username and return data for this user. + * @returns {Object} - user object + * @property {string} name - username + * @property {string} email - users email + * @property {string} pwHash - hashed password of the user + */ +async function getUserByEmail(email, alsoCheckUserName=true) { + return new Promise((resolve) => { + let sql = `SELECT name, email, pwHash password FROM User WHERE email is ?`; + db.get(sql, [email], (err, rows) => { + if (err) throw err; + //if no user with this email was found, check if a username with this string exists + if (rows == null && alsoCheckUserName) { + sql = `SELECT name, email, pwHash password FROM User WHERE name is ?`; + db.get(sql, [email], (err, rows) => { + if (err) throw err; + resolve(rows); + }); + } else resolve(rows); + }); + }); +} + +module.exports = {getMatrixUserByName, getUserByEmail, startDb}; \ No newline at end of file