From 2f726a5331bce373fee82e061e8cd0c01dea4107 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Wed, 31 May 2023 06:11:50 -0400 Subject: [PATCH] start support for songs --- dbTools/songs.js | 148 ++++++++++++++++++++++ def/profile.js | 19 +-- def/songs.js | 24 ++++ index.js | 2 + mongoDB.js | 3 + package-lock.json | 313 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + routes/songs.js | 87 +++++++++++++ socketio.js | 61 +++++++++ 9 files changed, 640 insertions(+), 18 deletions(-) create mode 100644 dbTools/songs.js create mode 100644 def/songs.js create mode 100644 routes/songs.js create mode 100644 socketio.js diff --git a/dbTools/songs.js b/dbTools/songs.js new file mode 100644 index 0000000..be78353 --- /dev/null +++ b/dbTools/songs.js @@ -0,0 +1,148 @@ +const DBName = "EMI_SOCIAL"; +const Song = require("./../def/songs.js") + +postDB = (DB)=>{ + DB.songsCol = DB.db.db(DBName).collection("songs"); + + DB.newSong = (songObj) => { + return DB.songsCol.insertOne(songObj.toObj()).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.removeSong = (songId) => { + if(!DB.ObjectID.isValid(songId)) return false; + const _id = DB.ObjectID(songId); + return DB.songsCol.deleteOne({_id}).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.updateSongContent = (songId, newContent, oldContent) => { + if(!DB.ObjectID.isValid(songId)) return false; + const id = DB.ObjectID(songId); + let update = { + $set:{ + content: newContent, + // lastUpdated: new Date() // add back when finish updating videos. + }, + $push: { + contentHistory: oldContent + } + } + return DB.songsCol.updateOne({_id: id}, update).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.newReaction = (songId, profileid, reaction) => { + if(!DB.ObjectID.isValid(songId)) return false; + const id = DB.ObjectID(songId); + let update = { + $set:{ + ["reactions." + profileid]: reaction, + lastUpdated: new Date() + } + } + return DB.songsCol.updateOne({_id: id}, update).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.removeReaction = (songId, profileid) => { + if(!DB.ObjectID.isValid(songId)) return false; + const id = DB.ObjectID(songId); + let update = { + $unset:{ + ["reactions." + profileid]: "" + }, + //$set: { //Maybe is not relevant to pump post here + // lastUpdated: new Date() + //} + } + return DB.songsCol.updateOne({_id: id}, update).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.newComment = (songId, comment) => { + if(!DB.ObjectID.isValid(songId)) return false; + const id = DB.ObjectID(songId); + return DB.songsCol.updateOne({_id: id}, { + $push: { + comments: comment + }, + $set: { + lastUpdated: new Date() + } + }).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.newCommentReaction = (songId, commentDate, profileid, reaction) => { + if(!DB.ObjectID.isValid(songId)) return false; + const id = DB.ObjectID(songId); + let update = { + $set:{ + ["comments.$.reactions." + profileid]: reaction, + "comments.$.lastUpdated": new Date(), + lastUpdated: new Date() + } + } + return DB.songsCol.updateOne({ + _id: id, + "comments.createdAt": commentDate + }, update).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.removeCommentReaction = (songId, commentDate, profileid) => { + if(!DB.ObjectID.isValid(songId)) return false; + const id = DB.ObjectID(songId); + let update = { + $unset:{ + ["comments.$.reactions." + profileid]: '', + "comments.$.lastUpdated": new Date(), + }, + $set: { + lastUpdated: new Date() + } + } + return DB.songsCol.updateOne({ + _id: id, + "comments.createdAt": commentDate + }, update).catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.getSongs = async (limit = 20) => { + let query = {}; + return DB.songsCol.find(query).sort({_id: -1}).limit(limit).toArray().catch((err)=>{ + console.log(err); + return false; + }); + } + + DB.getSong = (songId) => { + if(!DB.ObjectID.isValid(songId)) return []; + let _id = DB.ObjectID(songId); + return DB.songsCol.findOne({_id}).catch((err)=>{ + console.log(err); + return false; + }); + } + +} + +module.exports = postDB; \ No newline at end of file diff --git a/def/profile.js b/def/profile.js index 497e8bf..be310e7 100644 --- a/def/profile.js +++ b/def/profile.js @@ -28,24 +28,7 @@ class User { } toObj(){ - let r = {}; - r.userid = this.userid - r.username = this.username; - r.profile = this.profile; - r.data = this.data; - r.username = this.username; - r.following = this.following; - r.lastUpdate = this.lastUpdate; - r.newsFeedCache = this.newsFeedCache; - r.notifications = this.notifications; - - r.isGroup = this.isGroup; - r.isCourse = this.isCourse; - r.isPrivate = this.isPrivate; - r.isChat = this.isChat; - r.subscribed = this.subscribed; - r.pending = this.pending; - return r; + return { ...this }; } } diff --git a/def/songs.js b/def/songs.js new file mode 100644 index 0000000..2a2e07e --- /dev/null +++ b/def/songs.js @@ -0,0 +1,24 @@ +class Songs { + constructor(info) { + if (!info || !info.userid) throw "Can not construct empty profile"; + this.userid = info.userid; + this.data = info.data || {}; + + const allowedParams = ["title", 'author', "content", "contentHistory", "bpm", "lang", "reactions", "root", 'beatsPerTempo']; + for (const key in info) { + if (info.hasOwnProperty(key) && allowedParams.includes(key)) { + this[key] = info[key]; + } + } + this.createdAt = info.createdAt || new Date(); + this.lastUpdated = info.lastUpdated || this.createdAt; + this.comments = info.comments || []; + this.reactions = info.reactions || {}; + } + + toObj() { + return { ...this }; + } +} + +module.exports = Songs; \ No newline at end of file diff --git a/index.js b/index.js index 77ed9ef..ef67919 100644 --- a/index.js +++ b/index.js @@ -47,6 +47,7 @@ const Post = require("./def/post.js") const Profile = require("./def/profile.js"); const profileRoute = require('./routes/profile.js'); const postRoute = require('./routes/post.js'); +const songsRoute = require('./routes/songs.js'); const paymentsRoute = require('./routes/payments.js'); const subsplashRoute = require('./routes/subsplash.js'); const bibleRoute = require('./routes/bible.js'); @@ -258,6 +259,7 @@ DB.getDB.then((DB) => { app.use('/post', sessionChecker, postRoute); app.use('/payments', sessionChecker, paymentsRoute); app.use('/bible', sessionChecker, bibleRoute); + app.use('/songs', sessionChecker, songsRoute); //Public Routes app.use('/subsplash', subsplashRoute); diff --git a/mongoDB.js b/mongoDB.js index 23b2eb2..c9012e4 100644 --- a/mongoDB.js +++ b/mongoDB.js @@ -7,6 +7,8 @@ const postDB = require("./dbTools/post.js"); const profileDB = require("./dbTools/profile.js"); const paymentDB = require("./dbTools/payments.js"); const loggerDB = require("./dbTools/logger.js"); +const songsDB = require("./dbTools/songs.js"); + const getDB = new Promise((resolve, reject) => { @@ -116,6 +118,7 @@ const getDB = new Promise((resolve, reject) => { profileDB(DB); paymentDB(DB); loggerDB(DB); + songsDB(DB); resolve(DB); }); diff --git a/package-lock.json b/package-lock.json index 0585f28..1e71b74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "mongodb": "^3.6.3", "nodemailer": "^6.6.3", "object-hash": "^3.0.0", + "socket.io": "^4.6.1", "stripe": "^8.178.0", "web-push": "^3.4.5" } @@ -44,6 +45,24 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "16.10.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", @@ -156,6 +175,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bcrypt": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", @@ -531,6 +558,63 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", + "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -1395,6 +1479,84 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "node_modules/socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -1558,6 +1720,26 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -1581,6 +1763,24 @@ "tar": "^6.1.0" } }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "16.10.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", @@ -1678,6 +1878,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, "bcrypt": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", @@ -1959,6 +2164,48 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "engine.io": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", + "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==" + }, "entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -2601,6 +2848,66 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "requires": { + "ws": "~8.11.0" + } + }, + "socket.io-parser": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -2728,6 +3035,12 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index aaa644c..ee18aa4 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "mongodb": "^3.6.3", "nodemailer": "^6.6.3", "object-hash": "^3.0.0", + "socket.io": "^4.6.1", "stripe": "^8.178.0", "web-push": "^3.4.5" } diff --git a/routes/songs.js b/routes/songs.js new file mode 100644 index 0000000..760cbc5 --- /dev/null +++ b/routes/songs.js @@ -0,0 +1,87 @@ +var express = require('express') +var router = express.Router() + +const DB = require("../mongoDB.js"); +const Song = require("../def/songs.js"); + +DB.getDB.then((DB)=>{ + + const getUserId = function(req){ + const user_sid = req.cookies.user_sid || req.query.user_sid || req.body.user_sid; + return DB.ObjectID(user_sid); + } + + const getProfileId = (req)=>{ + return DB.ObjectID(req.cookies.profile_id || req.query.profile_id || req.body.profile_id); + } + + router.get("/", async (req, res) => { + let profileId = req.params.id; + let songs = await DB.getSongs(); + return res.json({ + status: "ok", + songs + }); + }); + + router.post("/", async (req, res) => { + let post = { + userid: getUserId(req), + ...req.body + } + let postObj = new Song(post); + let dbr = await DB.newSong(postObj); + song = postObj.toObj(); + song._id = dbr.insertedId; + return res.json({ + status: "ok", + ...song + }) + }); + + router.get("/:id", async (req, res) => { + let profileId = req.params.id; + let profile = await DB.getProfile(profileId); + return res.json({ + status: "ok", + ... profile + }); + }); + + async function songBelongsToUser(songId, userid){ + // TODO: Verify ownserhip + return true; + } + + router.delete("/:id", async (req, res) => { + const userid = getUserId(req); + const songId = req.params.id; + if(!await songBelongsToUser(songId, userid)) + return res.json({ + status: "This profile is not yours." + }); + await DB.removeSong(songId); + return res.json({ + status: "ok" + }); + }); + + router.post("/:id", async (req, res) => { + const userid = getUserId(req); + const songId = req.params.id; + const song = await DB.getSong(songId); + const newContent = req.body.content; + console.log("Updating song", newContent) + if(!await songBelongsToUser(songId, userid)) + return res.json({ + status: "This post is not yours." + }); + await DB.updateSongContent(songId, newContent, song.content); + return res.json({ + status: "ok" + }); + }); + +}); + +module.exports = router \ No newline at end of file diff --git a/socketio.js b/socketio.js new file mode 100644 index 0000000..60d4fcb --- /dev/null +++ b/socketio.js @@ -0,0 +1,61 @@ +// Server-side code +const express = require('express'); +const http = require('http'); +const socketIO = require('socket.io'); + +const app = express(); +const server = http.createServer(app); +const io = socketIO(server); + +let interval; +let tempo = 120; // Initial tempo (beats per minute) +let startTime; // Start time of the metronome + +io.on('connection', (socket) => { + console.log('A client connected'); + + socket.on('start', () => { + console.log('Metronome started'); + startTime = Date.now(); // Set the start time + + // Calculate the delay between each beat based on the tempo + const delay = (60 / tempo) * 1000; + + // Send a beat event to all connected clients at the specified interval + interval = setInterval(() => { + const elapsedTime = Date.now() - startTime; + io.emit('beat', elapsedTime); + }, delay); + }); + + socket.on('stop', () => { + console.log('Metronome stopped'); + clearInterval(interval); + }); + + socket.on('tempo', (newTempo) => { + console.log(`Tempo set to ${newTempo} BPM`); + tempo = newTempo; + + // If the metronome is already running, restart it with the new tempo + if (interval) { + clearInterval(interval); + const delay = (60 / tempo) * 1000; + interval = setInterval(() => { + const elapsedTime = Date.now() - startTime; + io.emit('beat', elapsedTime); + }, delay); + } + }); + + socket.on('disconnect', () => { + console.log('A client disconnected'); + clearInterval(interval); + }); +}); + +server.listen(3000, () => { + console.log('Server is running on port 3000'); +}); + +return io; \ No newline at end of file