2
0
Эх сурвалжийг харах

Initial commit of new code base

Cameron Kline 8 жил өмнө
parent
commit
2606273e1a

+ 6 - 0
.gitignore

@@ -85,3 +85,9 @@ fabric.properties
 
 # Created by .ignore support plugin (hsz.mobi)
 
+Thumbs.db
+.DS_Store
+
+.vagrant/
+rethinkdb_data/
+config/default.json

+ 59 - 1
README.md

@@ -1,2 +1,60 @@
 # MusareNode
-Musare in NodeJS.
+Musare in NodeJS, Express, SocketIO and VueJS.
+
+## Requirements
+ * [Virtualbox](https://www.virtualbox.org/)
+ * [Vagrant](https://www.vagrantup.com/)
+
+##### Important Notes
+The latest version of Vagrant (1.8.5) has some issues with inserting ssh keys into the machine. It's a show stopping bug that they aren't going to fix until the [next release](https://github.com/mitchellh/vagrant/issues/7610#issuecomment-234609660). So for now, I recommend using [Vagrant 1.8.4](https://releases.hashicorp.com/vagrant/1.8.4/). You'll also need to use a slightly [older version of Virtualbox](https://www.virtualbox.org/wiki/Download_Old_Builds_5_0) because of this.
+
+## Getting Started
+Once you've installed the required tools, pull down this repo, cd into it, then copy `config/template.json` to `config/default.json`. The `secret` key can be whatever. It's used by express's session module. The `apis.youtube.key` value can be obtained by going to https://developers.google.com/youtube/v3/getting-started and obtaining a youtube api key.
+
+After you've copied your config, run:
+
+`vagrant up`
+
+Once that's done go ahead and call:
+
+`vagrant reload`
+
+This will ensure that the services we've created start up correctly.
+
+Once this is done you should be able to access Musare in your local browser at [172.16.1.2](http://172.16.1.2). To access the RethinkDB admin panel, go to [172.16.1.2:8080](http://172.16.1.2:8080) in your local web browser.
+
+You can also now access your machine using:
+
+`vagrant ssh`
+
+Or if you have [mosh](https://mosh.org/) installed (and have ran `vagrant plugin install vagrant-mosh`), you can run:
+
+`vagrant mosh`
+
+You can run `vagrant` to view more options.
+
+### Logs
+
+You can view logs at the following locations:
+
+* Musare: `/var/log/upstart/musare.log`
+* RethinkDB: `/var/log/upstart/rethinkdb.log`
+
+### Development
+
+Ideally we'd have the app running using [nodemon](http://nodemon.io/). But for whatever reason, it doesn't restart when server code is changed on Ubuntu 14.04. If you find a solution for this let us know! Or make a pull request :)
+
+Because of this, you'll need to manually restart the nodejs app. You can do this by calling `sudo service musare restart` on the machine (make sure to ssh into the machine before calling this by running `vagrant ssh`).
+
+You'll probably want to have two terminal windows open, one that you can restart Musare with. And another that you can run `sudo tail -F /var/log/upstart/musare.log` in. This will output everything that running `node app.js` would typically output.
+
+## FAQ
+
+##### What does `vagrant up` do?
+This will pull down the Ubuntu 14.04 vagrant box and setup a virtualbox machine for you. It'll ask you what network interface you want the virtualbox machine to connect to the internet with. On macOS I typically choose `en0: Wi-Fi (AirPort)`, but it'll be different on different platforms. It will then run the commands in the `bootstrap.sh` file on this virtual machine. This will install nodejs, rethinkdb, and a bunch of other goodies. This same file could be ran on a production Ubuntu 14.04 server with very little modifications (if any at all).
+
+##### What does `vagrant ssh` and `vagrant mosh` do?
+Vagrant automagically generates and inserts a openssh keypair for you. This doesn't really have any security (as it doesn't really need to be since it's only for development). This allows you to access your machine in a very convenient way. The second command, `vagrant mosh`, is actually just a vagrant plugin. Mosh is a replacement for SSH, and if you haven't checked it out before, you really should! :)
+
+##### Why use Vagrant? I can run NodeJS and RethinkDB locally
+The reason for using vagrant is simple. It gives every developer the same local development server. This removes any inconsistencies across different dev enviroments (Windows vs macOS vs Linux). It also ensures that your changes are running on an enviroment that exactly matches the production server.

+ 25 - 0
Vagrantfile

@@ -0,0 +1,25 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+HOST_SSH_PORT = 2260
+
+Vagrant.configure(2) do |config|
+
+	config.vm.box = "ubuntu/trusty64"
+
+	config.vm.network "public_network"
+
+	config.vm.network "private_network", :ip => '172.16.1.2'
+
+	config.vm.network :forwarded_port, guest: 22, host: 2222, id: "ssh", disabled: true
+	config.vm.network :forwarded_port, guest: 22, host: HOST_SSH_PORT, auto_correct: true
+
+	config.ssh.port = HOST_SSH_PORT
+	config.ssh.guest_port = 22
+
+	config.vm.synced_folder '.', '/vagrant', disabled: true
+	config.vm.synced_folder ".", "/musare"
+
+	config.vm.provision "shell", path: "bootstrap.sh"
+
+end

+ 43 - 0
app.js

@@ -0,0 +1,43 @@
+'use strict';
+
+// nodejs modules
+const path = require('path'),
+      fs   = require('fs'),
+      os   = require('os');
+
+// npm modules
+const express    = require('express'),
+      session    = require('express-session'),
+      bodyParser = require('body-parser'),
+      config     = require('config'),
+      request    = require('request'),
+      r          = require('rethinkdb');
+
+// custom modules
+const socketManager = require('./logic/socketManager');
+
+// setup express and socket.io
+const app = express();
+const server = app.listen(80);
+const io = require('socket.io')(server);
+
+// connect to our database before doing anything else
+r.connect( { host: 'localhost', port: 28015, db: 'musare' }, (err, conn) => {
+	if (err) {
+		console.log(err);
+	}
+	else {
+		app.use(session({
+			resave: true,
+			saveUninitialized: false,
+			secret: config.get("secret"),
+			cookie: { httpOnly: true, maxAge: 2419200000 }
+		}));
+
+		app.use(express.static(__dirname + '/public'));
+
+		io.on('connection', (socket) => {
+			socketManager.handle(socket, io, conn);
+		});
+	}
+});

+ 100 - 0
bootstrap.sh

@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+
+function command_exists { type "$1" &> /dev/null; }
+
+# install NodeJS
+if command_exists "mosh"; then
+	echo "Skipping mosh install"
+else
+	echo "Installing mosh"
+	sudo apt-get install -y mosh
+fi
+
+# install NodeJS
+if command_exists "nodejs"; then
+	echo "Skipping nodejs install"
+else
+	echo "Installing nodejs"
+	curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
+	sudo apt-get install -y nodejs
+fi
+
+# install RethinkDB
+if command_exists "rethinkdb"; then
+	echo "Skipping rethinkdb install"
+else
+	echo "Installing rethinkdb"
+	source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list
+	wget -qO- https://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add -
+	sudo apt-get update
+	sudo apt-get install -y rethinkdb
+fi
+
+# setup a service for RethinkDB
+if [ -f /etc/init/rethinkdb.conf ]; then
+	echo "Skipping up rethinkdb service"
+else
+	echo "Setting up rethinkdb service"
+	sudo tee -a /etc/init/rethinkdb.conf > /dev/null <<EOF
+description "Service file for starting / stopping rethinkdb"
+author "Musare Developers"
+
+start on filesystem
+stop on shutdown
+
+setgid rethinkdb
+console log
+
+script
+	echo \$\$ > /var/run/rethinkdb.pid
+	cd /musare
+	exec rethinkdb --bind all
+end script
+
+pre-start script
+	echo "[\`date\`] rethinkdb starting" >> /var/log/rethinkdb.log
+end script
+
+pre-stop script
+	rm /var/run/rethinkdb.pid
+	echo "[\`date\`] rethinkdb stopping" >> /var/log/rethinkdb.log
+end script
+EOF
+fi
+
+# setup a service for Musare
+if [ -f /etc/init/musare.conf ]; then
+	echo "Skipping up musare service"
+else
+	echo "Setting up musare service"
+	sudo tee -a /etc/init/musare.conf > /dev/null <<EOF
+description "Service file for starting / stopping musare"
+author "Musare Developers"
+
+start on filesystem
+stop on shutdown
+
+setgid www-data
+console log
+
+script
+	until mountpoint -q /musare; do sleep 1; done
+	echo \$\$ > /var/run/musare.pid
+	cd /musare
+	exec node app.js
+end script
+
+pre-start script
+	echo "[\`date\`] musare starting" >> /var/log/musare.log
+end script
+
+pre-stop script
+	rm /var/run/musare.pid
+	echo "[\`date\`] musare stopping" >> /var/log/musare.log
+end script
+EOF
+fi
+
+# automatically install all of our dependencies
+cd /musare
+npm install

+ 0 - 26
client/html/index.html

@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>Title</title>
-</head>
-<body>
-<h1>Musare!</h1>
-<h1>Passport/RethinkDB Example</h1>
-{{#user}}
-<p>You are logged in as <span style="font-weight: bold;">{{ username }}</span>. Account created through <span style="font-weight: bold;">{{ type }}</span>.</p>
-<a href='/auth/user'>See user data</a>
-</br>
-<a href='/auth/logout'>Logout</a>
-{{/user}}
-{{^user}}
-<p>You are not logged in.</p>
-<a href='/auth/login/github'>Login with GitHub</a>
-<form action="/auth/login">
-    <input id="username" name="username" type="text">
-    <input id="password" name="password" type="password">
-    <button type="submit">Submit</button>
-</form>
-{{/user}}
-</body>
-</html>

+ 0 - 14
client/html/login.html

@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>Login</title>
-</head>
-<body>
-<form action="/auth/login">
-    <input id="username" name="username" type="text">
-    <input id="password" name="password" type="password">
-    <button type="submit">Submit</button>
-</form>
-</body>
-</html>

+ 0 - 18
client/html/register.html

@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>Register</title>
-</head>
-<body>
-<form action="/auth/register">
-    <label for="username">Username</label>
-    <input id="username" name="username" type="text">
-    <label for="email">Email</label>
-    <input id="email" name="email" type="text">
-    <label for="password">Password</label>
-    <input id="password" name="password" type="password">
-    <button type="submit">Submit</button>
-</form>
-</body>
-</html>

+ 8 - 0
config/template.json

@@ -0,0 +1,8 @@
+{
+	"secret": "",
+	"apis": {
+		"youtube": {
+			"key": ""
+		}
+	}
+}

+ 142 - 0
logic/socketManager.js

@@ -0,0 +1,142 @@
+'use strict';
+
+// nodejs modules
+const path = require('path'),
+      fs   = require('fs'),
+      os   = require('os');
+
+// npm modules
+const config    = require('config'),
+      request   = require('request'),
+      waterfall = require('async/waterfall').
+      r         = require('rethinkdb');
+
+// custom modules
+const utils = require('./utils');
+
+module.exports = {
+
+	setup: (io, rc) => {
+
+		r.table('comments')
+
+	},
+
+	handle: (socket, io, rc) => {
+
+		socket.custom = {};
+
+		socket.on('disconnect', () => {
+
+		});
+
+		socket.on('login', (user) => {
+
+			if (!user.username || !user.password) {
+				socket.emit('login', { status: 'error', message: 'Invalid login request' });
+				return;
+			}
+
+			r.table('users').filter({
+				username: user.username, password: crypto.createHash('md5').update(user.password).digest("hex")
+			}).run(rc, (err, cursor) => {
+				if (err) {
+					socket.emit('login', { status: 'failure', message: 'Error while fetching the user' });
+				}
+				else {
+					cursor.toArray((err, result) => {
+						if (err) {
+							socket.emit('login', { status: 'failure', message: 'Error while fetching the user' });
+						}
+						else {
+							socket.emit('login', { status: 'success', user: result });
+						}
+					});
+				}
+			});
+		});
+
+		socket.on('register', (user) => {
+
+			console.log(user);
+
+			if (!user.email || !user.username || !user.password) {
+				socket.emit('register', { status: 'error', message: 'Invalid register request' });
+				return;
+			}
+		});
+
+		socket.on('rooms', () => {
+			r.table('rooms').run(rc, (err, cursor) => {
+				if (err) {
+					socket.emit('rooms', { status: 'failure', message: 'Error while fetching the rooms' });
+				}
+				else {
+					cursor.toArray((err, result) => {
+						if (err) {
+							socket.emit('rooms', { status: 'failure', message: 'Error while fetching the rooms' });
+						}
+						else {
+							socket.emit('rooms', result);
+						}
+					});
+				}
+			});
+		});
+
+		socket.on('room', (id) => {
+
+			if (socket.custom.user == null) {
+				socket.emit('room', { status: 'error', message: "You can't join a room until you've logged in" });
+				return;
+			}
+
+			r.table('rooms').get(id).run(rc, (err, result) => {
+				if (err) {
+					socket.emit('room', { status: 'error', message: 'Room with that id does not exist' });
+				}
+				else {
+					socket.custom.roomId = id;
+
+					var userInfo = {
+						username: socket.custom.user.username
+					};
+
+					var otherUsersInfo = [];
+
+					// tell all the users in this room that someone is joining it
+					io.sockets.clients().forEach((otherSocket) => {
+						if (otherSocket != socket && otherSocket.custom.roomId == id) {
+							otherUsersInfo.push({ username: otherSocket.custom.user.username });
+							otherSocket.emit('room', { status: 'joining', user: userInfo });
+						}
+					});
+
+					socket.emit('room', { status: 'joined', data: {
+						room: result, users: otherUsersInfo
+					}});
+				}
+			});
+		});
+
+		socket.on('search', (query) => {
+			request('https://www.googleapis.com/youtube/v3/search?' + [
+				'part=snippet', `q=${encodeURIComponent(query)}`, `key=${config.get('apis.youtube.key')}`, 'type=video', 'maxResults=25'
+			].join('&'), (err, res, body) => {
+				if (err) {
+					socket.emit('search', { status: 'error', message: 'Failed to make request' });
+				}
+				else {
+					try {
+						socket.emit('search', { status: 'success', body: JSON.parse(body) });
+					}
+					catch (e) {
+						socket.emit('search', { status: 'error', message: 'Non JSON response' });
+					}
+				}
+			});
+		});
+
+		socket.emit('ready');
+	}
+};

+ 3 - 0
logic/utils.js

@@ -0,0 +1,3 @@
+module.exports = {
+
+};

+ 15 - 23
package.json

@@ -1,25 +1,17 @@
 {
-  "name": "musare",
-  "version": "1.0.0",
-  "description": "Musare.",
-  "main": "server.js",
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "author": "Musare",
-  "license": "ISC",
-  "dependencies": {
-    "bcryptjs": "^2.3.0",
-    "consolidate": "^0.14.1",
-    "express": "^4.14.0",
-    "express-session": "^1.14.0",
-    "express-validator": "^2.20.8",
-    "mustache": "^2.2.1",
-    "node-sass": "^3.8.0",
-    "passport": "^0.3.2",
-    "passport-github": "^1.1.0",
-    "passport-local": "^1.0.0",
-    "rethinkdb": "^2.3.2",
-    "rethinkdb-init": "^0.2.2"
-  }
+	"name": "musare",
+	"version": "0.0.0",
+	"description": "",
+	"main": "app.js",
+	"repository": "",
+	"dependencies": {
+		"async": "2.0.1",
+		"body-parser": "^1.15.2",
+		"config": "^1.21.0",
+		"express": "^4.14.0",
+		"express-session": "^1.14.0",
+		"request": "^2.74.0",
+		"socket.io": "^1.4.8",
+		"rethinkdb": "^2.3.2"
+	}
 }

+ 139 - 0
public/css/app.css

@@ -0,0 +1,139 @@
+* { box-sizing: border-box; font-family: Roboto, sans-serif; }
+html, body { margin: 0; padding: 0; width: 100%; height: 100%; }
+
+#app { background-color: white; width: 100%; height: 100%; }
+.loading { background-color: white; width: 100%; height: 100%; background-image: url("../images/loading.gif"); background-position: center; background-size: 128px 128px; background-repeat: no-repeat; }
+
+.header {
+	width: 100%;
+	height: 64px;
+	line-height: 64px;
+	text-align: center;
+	box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
+}
+
+.header-home { width: 100%; height: 100%; background-color: #424242; }
+.header-home-title { float: left; height: 100%; color: white; padding: 0 16px 0 16px; font-weight: 300; font-size: 32px; cursor: pointer; }
+.header-home-link { z-index: 101; position: relative; float: right; height: 100%; color: white; padding: 0 16px 0 16px; cursor: pointer; }
+.header-home-link-title {  }
+.header-home-title:hover, .header-home-link:hover { cursor: pointer; background-color: #393939; }
+
+.header-room { width: 100%; height: 100%; background-color: #0091ea; }
+.header-room-linkLeft { float: left; width: 64px; color: white; text-decoration: none; font-weight: 300; font-size: 28px; }
+.header-room-linkCenter { display: inline-block; margin: 0 auto; color: white; text-decoration: none; font-weight: 300; font-size: 32px; }
+.header-room-linkRight { float: right; width: 64px; color: white; text-decoration: none; font-weight: 300; font-size: 28px; }
+.header-room-linkLeft:hover, .header-room-linkRight:hover { cursor: pointer; background-color: #0083d3; }
+
+.body {}
+
+.body-home { width: 100%; height: auto; }
+.body-home-group { width: 100%; height: 448px; margin: 64px 0 64px 0; }
+.body-home-group-title { float: left; clear: none; width: 100%; height: 64px; line-height: 48px; text-align: center; font-size: 48px; }
+.body-home-group-nav { background-color: transparent; color: rgba(0, 0, 0, 0.75); float: left; clear: none; width: 64px; height: 384px; line-height: 384px; text-align: center; font-size: 64px; cursor: pointer; }
+.body-home-group-nav:hover { color: #222222; }
+.body-home-group-rooms { white-space: nowrap; text-align: center; overflow: hidden; float: left; clear: none; width: calc(100% - 128px); height: 384px; }
+
+.body-home-group-rooms-room {
+	position: relative;
+	top: 16px;
+	display: inline-block;
+	clear: none;
+	width: 256px;
+	height: 352px;
+	margin: 0 16px 0 16px;
+	box-shadow: 0 1px 6px 2px rgba(0, 0, 0, 0.25);
+	cursor: pointer;
+}
+
+.body-home-group-rooms-room-image { width: 100%; height: 256px; border-bottom: 1px solid #d2d2d2; }
+.body-home-group-rooms-room-info { width: 100%; height: 96px; }
+.body-home-group-rooms-room-info-name { float: left; font-size: 25px; padding-left: 16px; text-align: left; width: 160px; height: 48px; line-height: 60px; }
+.body-home-group-rooms-room-info-description { float: left; font-size: 15px; padding-left: 16px; text-align: left; width: 160px; height: 48px; line-height: 36px; }
+.body-home-group-rooms-room-info-users { float: right; text-align: center; width: 96px; height: 96px; line-height: 96px; }
+
+.body-room {}
+
+.body-room-player {}
+
+.body-room-playlist {}
+.body-room-playlist-song {}
+
+.body-room-chat {}
+.body-room-chat-message {}
+.body-room-chat-input {}
+.body-room-chat-send {}
+
+.body-room-users {}
+
+.body-overlay {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(0, 0, 0, 0.5);
+	z-index: 90000;
+	cursor: pointer;
+}
+
+.body-modal {
+	position: fixed;
+	display: block;
+	box-shadow: 0 1px 6px 2px rgba(0, 0, 0, 0.25);
+	background-color: white;
+	z-index: 90001;
+	padding: 12px;
+	transition: top 0.5s ease;
+	overflow: hidden;
+}
+
+.body-modal-register-title, .body-modal-register-input, .body-modal-register-recaptcha, .body-modal-register-submit {
+	display: inline-block;
+	width: calc(100% - 24px);
+}
+.body-modal-register-input, .body-modal-register-recaptcha, .body-modal-register-submit {
+	margin: 8px 12px;
+}
+.body-modal-register-title, .body-modal-register-input, .body-modal-register-submit {
+	height: 42px; line-height: 42px;
+}
+.body-modal-register-title { text-align: center; font-size: 32px; margin: 0 12px; }
+.body-modal-register-exit { cursor: pointer; position: absolute; right: 8px; top: 8px; font-size: 24px; }
+.body-modal-register-input { outline: none; border: 1px solid #d3d3d3; padding-left: 8px; font-size: 16px; }
+.body-modal-register-recaptcha { display: block; height: 76px; overflow: hidden; }
+.body-modal-register-submit { cursor: pointer; color: white; background-color: #1976d2; font-size: 24px; text-align: center; box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.1); }
+.body-modal-register-submit:hover { box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.25); }
+
+.body-modal-account-button {
+	float: left;
+	width: 144px;
+	height: 144px;
+	line-height: 144px;
+	text-align: center;
+	background-color: #e9e9e9;
+	border: 1px solid #d3d3d3;
+	border-radius: 5px;
+	margin: 12px;
+	color: #3a3a3a;
+	cursor: pointer;
+}
+.body-modal-account-button:hover {
+	background-color: #dfdfdf;
+}
+
+.footer { width: 100%; height: 200px; background-color: #e0e0e0; padding-top: 26px; }
+
+.footer-left { float: left; width: 50%; height: 150px; padding-left: 64px; }
+.footer-left-title { font-size: 25px; color: #616161; margin-bottom: 16px; }
+.footer-left-message { font-size: 15px; color: #757575; margin-bottom: 16px; }
+.footer-left-icons {}
+.footer-left-icons-icon { float: left; font-size: 32px; padding-right: 16px; color: #757575; }
+.footer-left-icons-icon:hover { cursor: pointer; color: #222222; }
+
+.footer-right { float: right; width: 50%; height: 150px; padding-left: 64px; }
+.footer-right-links { float: right; width: 200px; }
+.footer-right-links-title { float: right; width: 200px; font-size: 25px; color: #616161; margin-bottom: 8px; }
+.footer-right-links-link { float: right; width: 200px; clear: both; font-size: 15px; color: #757575; margin-bottom: 2px; }
+.footer-right-links-link:hover { cursor: pointer; color: #222222; }
+
+.footer-message { float: left; width: 100%; height: 50px; line-height: 50px; padding-left: 128px; background-color: #d3d3d3; color: #757575; }

BIN
public/images/loading.gif


+ 123 - 0
public/index.html

@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<title>Musare with NodeJS + Express + SocketIO + VueJS</title>
+		<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.min.js"></script>
+		<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
+		<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
+		<script src='https://www.google.com/recaptcha/api.js'></script>
+		<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">
+		<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
+		<link type="text/css" rel="stylesheet" href="css/app.css" />
+		<script src="js/app.js"></script>
+	</head>
+	<body>
+		<div id="app">
+			<div class="loading" style="display: block;" v-show="!(home.visible || room.visible)"></div>
+			<div class="header" style="display: none;" v-show="home.visible || room.visible">
+				<div class="header-home" v-show="home.visible">
+					<div class="header-home-title">Musare</div>
+					<div class="header-home-link" v-on:click="showModal('account')">Account&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-caret-down" aria-hidden="true"></i></div>
+					<div class="header-home-link" v-on:click="showModal('donate')">Donate</div>
+					<div class="header-home-link" v-on:click="showModal('project')">The Project</div>
+				</div>
+				<div class="header-room" v-show="room.visible">
+					<div class="header-room-linkLeft" v-on:click="leaveRoom"><i class="fa fa-home" aria-hidden="true"></i></div>
+					<div class="header-room-linkLeft"><i class="fa fa-plus" aria-hidden="true"></i></div>
+					<div class="header-room-linkLeft"><i class="fa fa-flag" aria-hidden="true"></i></div>
+					<div class="header-room-linkLeft"><i class="fa fa-step-forward" aria-hidden="true"></i></div>
+					<div class="header-room-linkCenter">{{ room.name }}</div>
+					<div class="header-room-linkRight"><i class="fa fa-users" aria-hidden="true"></i></div>
+					<div class="header-room-linkRight"><i class="fa fa-comment" aria-hidden="true"></i></div>
+					<div class="header-room-linkRight"><i class="fa fa-list" aria-hidden="true"></i></div>
+				</div>
+			</div>
+			<div class="body" style="display: none;" v-show="home.visible || room.visible">
+				<div class="body-home" v-show="home.visible">
+					<div class="body-home-group" v-for="group in home.groups">
+						<div class="body-home-group-title">{{ group.name }}</div>
+						<div class="body-home-group-nav"><i class="fa fa-chevron-left" aria-hidden="true"></i></div>
+						<div class="body-home-group-rooms">
+							<div class="body-home-group-rooms-room" v-for="room in group.rooms" v-on:click="enterRoom(room)">
+								<div class="body-home-group-rooms-room-image"></div>
+								<div class="body-home-group-rooms-room-info">
+									<div class="body-home-group-rooms-room-info-name">{{ room.name }}</div>
+									<div class="body-home-group-rooms-room-info-users">{{ room.users }}&nbsp;&nbsp;<i class="fa fa-user" aria-hidden="true"></i></div>
+									<div class="body-home-group-rooms-room-info-description">{{ room.description }}</div>
+								</div>
+							</div>
+						</div>
+						<div class="body-home-group-nav"><i class="fa fa-chevron-right" aria-hidden="true"></i></div>
+					</div>
+				</div>
+				<div class="body-room" v-show="room.visible">
+					<div class="body-room-player">
+						<!-- Youtube stuff goes here -->
+					</div>
+					<div class="body-room-playlist">
+						<div class="body-room-playlist-song" v-for="song in room.playlist.songs">
+
+						</div>
+					</div>
+					<div class="body-room-chat">
+						<div class="body-room-chat-message" v-for="message in room.chat.messages">
+
+						</div>
+					</div>
+					<div class="body-room-users"></div>
+				</div>
+				<div class="body-overlay" v-show="modalVisible()" v-on:click="showModal()"></div>
+				<div class="body-modal" v-bind:style="modalPositionStyle('register', 350, 400)">
+					<div class="body-modal-register-title">Register</div>
+					<div class="body-modal-register-exit" v-on:click="showModal()"><i class="fa fa-times" aria-hidden="true"></i></div>
+					<input class="body-modal-register-input" type="text" placeholder="Email..." v-model="modals.register.email"/>
+					<input class="body-modal-register-input" type="text" placeholder="Username..." v-model="modals.register.username"/>
+					<input class="body-modal-register-input" type="password" placeholder="Password..." v-model="modals.register.password"/>
+					<div class="g-recaptcha body-modal-register-recaptcha" data-sitekey="6Ld5jygTAAAAAEi0E1IwovUuVFhZKctMvKnY553W"></div>
+					<div class="body-modal-register-submit" v-on:click="register()">Submit</div>
+				</div>
+				<div class="body-modal" v-bind:style="modalPositionStyle('account', 360, 200)">
+					<div class="body-modal-account-button" v-on:click="showModal('register')">Register</div>
+					<div class="body-modal-account-button" v-on:click="showModal('login')">Login</div>
+				</div>
+				<div class="body-modal" v-bind:style="modalPositionStyle('donate', 360, 200)">
+					<p>Donations! Woo! :)</p>
+				</div>
+				<div class="body-modal" style="padding: 24px;" v-bind:style="modalPositionStyle('project', 600, 468)">
+					<h2 style="margin: 0 0 8px 0;">What is Musare?</h2>
+					<p style="margin: 0 0 16px 0;">Musare is an open-source music website where you can listen to real-time genre specific music stations with your friends, or just alone.</p>
+					<h2 style="margin: 0 0 8px 0;">How can I help?</h2>
+					<p style="margin: 0;">There are essentially 3 main ways in which you can help us:</p>
+					<ol>
+						<li style="margin-bottom: 16px; text-align: justify;">If you are a JavaScript developer, then we can use your help! Our project is open source and all the source code can be found on GitHub. We would love for you to create new features, add exciting content or just improve what already is on Musare. Also, if you see a bug or glitch on Musare, then create a new "issue" on GitHub and we will fix it as soon as possible. You can also have a try at fixing a bug that you may find!</li>
+						<li style="margin-bottom: 16px; text-align: justify;">If you are not a developer but want some way of supporting us - you can donate to Musare. Any amount of donation is highly appreciated, and will help us maintain server costs.</li>
+						<li style="margin-bottom: 16px; text-align: justify;">Send us feedback! Your comments and/or suggestion are extremely valuable to us. In order to improve we need to know what you like, don't like or what you might want on the app.</li>
+					</ol>
+				</div>
+			</div>
+			<div class="footer" style="display: none;" v-show="home.visible || room.visible">
+				<div class="footer-left">
+					<div class="footer-left-title">Stay Connected</div>
+					<div class="footer-left-message">Follow us on social media or send us an email!</div>
+					<div class="footer-left-icons">
+						<div class="footer-left-icons-icon"><i class="fa fa-twitter" aria-hidden="true"></i></div>
+						<div class="footer-left-icons-icon"><i class="fa fa-facebook" aria-hidden="true"></i></div>
+						<div class="footer-left-icons-icon"><i class="fa fa-twitch" aria-hidden="true"></i></div>
+					</div>
+				</div>
+				<div class="footer-right">
+					<div class="footer-right-links">
+						<div class="footer-right-links-title">Links</div>
+						<a class="footer-right-links-link">Github</a>
+						<a class="footer-right-links-link">Feedback</a>
+						<a class="footer-right-links-link">FAQ</a>
+						<a class="footer-right-links-link">Terms</a>
+						<a class="footer-right-links-link">Privacy</a>
+					</div>
+				</div>
+				<div class="footer-message">© Copyright Musare: 2015 - {{ new Date().getFullYear() }}</div>
+			</div>
+		</div>
+	</body>
+</html>

+ 149 - 0
public/js/app.js

@@ -0,0 +1,149 @@
+window.onload = function () {
+
+	var socket = io();
+
+	socket.on('disconnect', function () {
+		console.log('Disconnected from server');
+	});
+
+	socket.on('search', function (res) {
+		console.log(res);
+	});
+
+	var data = {
+		user: {
+			id: null
+		},
+		modals: {
+			register: {
+				visible: false,
+				email: "",
+				username: "",
+				password: ""
+			},
+			account: {
+				visible: false
+			},
+			donate: {
+				visible: false
+			},
+			project: {
+				visible: false
+			}
+		},
+		home: {
+			visible: false,
+			groups: [
+				{
+					id: "lu08gw56571r4497wrk9",
+					name: "Official Rooms",
+					rooms: [
+						{ id: "73qvw65746acvo8yqfr", name: "Country", description: "Country Music", users: 10 },
+						{ id: "enxcysmhn1k7ld56ogvi", name: "Pop", description: "Pop Music", users: 14 },
+						{ id: "kqa99gbva7lij05dn29", name: "Chill", description: "Chill Music", users: 13 },
+						{ id: "w19hu791iiub6wmjf9a4i", name: "EDM", description: "EDM Music", users: 13 }
+					]
+				},
+				{
+					id: "g2b8v03xaedj8ht1emi",
+					name: "Trending Rooms",
+					rooms: [
+						{ id: "73qvw65746acvo8yqfr", name: "Country", description: "Country Music", users: 10 },
+						{ id: "enxcysmhn1k7ld56ogvi", name: "Pop", description: "Pop Music", users: 14 },
+						{ id: "kqa99gbva7lij05dn29", name: "Chill", description: "Chill Music", users: 13 },
+						{ id: "w19hu791iiub6wmjf9a4i", name: "EDM", description: "EDM Music", users: 13 }
+					]
+				}
+			]
+		},
+		room: {
+			visible: false,
+			id: "",
+			name: "",
+			description: "",
+			player: {
+
+			},
+			chat: {
+				visible: false,
+				messages: []
+			},
+			users: {
+				list: {}
+			},
+			playlist: {
+				visible: false,
+				messages: []
+			}
+		}
+	};
+
+	var methods = {
+		leaveRoom: function () {
+			data.room.visible = false;
+			window.setTimeout(function () { data.home.visible = true; }, 500);
+		},
+		enterRoom: function (room) {
+			data.home.visible = false;
+			window.setTimeout(function () { data.room.visible = true; }, 500);
+			data.room.id = room.id;
+			data.room.name = room.name;
+			data.room.description = room.description;
+		},
+		modalVisibilityChange: function (name) {
+			var modal = data.modals[name];
+			if (modal) {
+				switch (name) {
+					case 'register': {
+						data.modals.register.email = "";
+						data.modals.register.username = "";
+						data.modals.register.password = "";
+						grecaptcha.reset();
+					} break;
+				}
+			}
+		},
+		showModal: function (name) {
+			Object.keys(data.modals).forEach(function (key) {
+				var visibility = data.modals[key].visible;
+				data.modals[key].visible = (key == name);
+				if (visibility != data.modals[key].visible) {
+					methods.modalVisibilityChange(key);
+				}
+			});
+		},
+		modalVisible: function (name) {
+			var visible = false;
+			// check if the specific modal is visible
+			if (name) {
+				Object.keys(data.modals).forEach(function (key) { if (key == name) visible = data.modals[key].visible; });
+			}
+			// check if any of them are visible
+			else {
+				Object.keys(data.modals).forEach(function (key) { if (visible == false) visible = data.modals[key].visible; });
+			}
+			return visible;
+		},
+		modalPositionStyle: function (name, width, height) {
+			return {
+				width: width + 'px',
+				height: height + 'px',
+				left: 'calc(50% - ' + (width / 2) + 'px)',
+				top: (data.modals[name].visible ? 'calc(50% - ' + (height / 2) + 'px)' : '-' + (height + 32) + 'px')
+			}
+		},
+		register: function () {
+			socket.emit('register', {
+				email: data.modals.register.email,
+				username: data.modals.register.username,
+				password: data.modals.register.password,
+				recaptcha: grecaptcha.getResponse()
+			});
+		}
+	};
+
+	var app = new Vue({ el: '#app', data: data, methods: methods, ready: function () { window.setTimeout(function () { data.home.visible = true; }, 250); } });
+
+	window.socket = socket;
+	window.data = data;
+};

+ 0 - 20
server/auth/auth-controller.js

@@ -1,20 +0,0 @@
-var authController = {};
-//Function to get the user info and return it in json
-authController.getUser = function (req, res) {
-    if (req.user && req.user.id) {
-        res.json(req.user);
-        return;
-    }
-    res.status(400).json(null);
-};
-//Function to logout
-authController.logout = function (req, res) {
-    req.logout();
-    res.redirect('/');
-};
-//Function to login? Not sure, might be able to remove this or move some router functions to here?
-authController.login = function (req, res) {
-    res.redirect('/');
-};
-
-module.exports = authController;

+ 0 - 105
server/auth/auth-router.js

@@ -1,105 +0,0 @@
-var express = require('express');
-var authControllers = require('./auth-controller');
-
-var auth = require('./index');
-var authRouter = express.Router();
-var r = require('../db');
-
-var bcrypt = require('bcryptjs');
-
-//GitHub authentication routes
-//GitHub authentication callback route
-authRouter.use('/login/callback/github', auth.authenticate('github'), function (req, res) {
-    res.redirect('/');
-});
-//GitHub authentication route
-authRouter.get('/login/github', auth.authenticate('github'));
-
-//Local authentication routes
-//Local login route
-authRouter.get('/login', auth.authenticate('local', {successRedirect: '/auth/user', failureRedirect: '/login'}), function(req, res) {
-    // If this function gets called, authentication was successful.
-    // `req.user` contains the authenticated user.
-    res.redirect("/auth/user");
-});
-
-//Local register route
-authRouter.get('/register', function(req, res) {
-    //Checks if the email, username and password are valid
-    req.checkQuery('email', 'Invalid email').isEmail();
-    req.checkQuery('username', 'Invalid getparam').notEmpty();
-    req.checkQuery('password', 'Invalid getparam').notEmpty();
-
-    var query = req.query;
-
-    //Check to see if there are any errors, and throw them if so
-    var errors = req.validationErrors();
-    if (errors) {
-        res.send('There have been validation errors: ', 400);
-        return;
-    } else {
-        //TODO Check if username/email already exists
-        //Check to see if a user with that username already exists
-        r.table("users").getAll(query.username.toLowerCase(), {index: "usernameL"}).isEmpty().run(r.conn, function(err, result) {
-            if (err) throw err;
-            if (result) {
-                //Check to see if a user with that email already exists
-                r.table("users").getAll(query.email.toLowerCase(), {index: "email"}).isEmpty().run(r.conn, function(err, result) {
-                    if (err) throw err;
-                    if (result) {
-                        //TODO Hash password
-                        var hash;
-                        //Generating a salt
-                        bcrypt.genSalt(10, function (err, salt) {
-                            if (err) {
-                                //TODO Throw error
-                            } else {
-                                //Hashing the password with the salt
-                                bcrypt.hash(query.password, salt, function (err, hash) {
-                                    if (err) {
-                                        //TODO Throw error
-                                    } else {
-                                        var email = query.email.toLowerCase();
-                                        var usernameL = query.username.toLowerCase();
-                                        //Inserting the user object into the database
-                                        r.table('users')
-                                            .insert({
-                                                username: query.username,
-                                                usernameL: usernameL,
-                                                email: email,
-                                                type: 'local',
-                                                password: hash
-                                            })
-                                            .run(r.conn)
-                                            .then(function (response) {
-
-                                                return r.table('users')
-                                                    //Getting the newly created user
-                                                    .get(response.generated_keys[0])
-                                                    .run(r.conn);
-                                            })
-                                            .then(function (newUser) {
-                                                //Logging in
-                                                //TODO Log in
-                                            });
-                                    }
-                                });
-                            }
-                        });
-                    } else {
-                        //TODO Throw error
-                    }
-                });
-            } else {
-                //TODO Throw error
-            }
-        });
-    }
-});
-
-//Route to get user info
-authRouter.use('/user', authControllers.getUser);
-//Route to logout
-authRouter.use('/logout', authControllers.logout);
-
-module.exports = authRouter;

+ 0 - 146
server/auth/index.js

@@ -1,146 +0,0 @@
-var passport = require('passport');
-var GitHubStrategy = require('passport-github').Strategy;
-var LocalStrategy = require('passport-local').Strategy;
-var r = require('../db');
-var bcrypt = require('bcryptjs');
-
-//This stores the user id in the session as a reference, and is used to call deserializeUser when it needs all info
-passport.serializeUser(function (user, done) {
-    return done(null, user.id);
-});
-
-//This returns the user the user info from the user id
-passport.deserializeUser(function (id, done) {
-    r
-        .table('users')
-        .get(id)
-        .run(r.conn)
-        .then(function (user) {
-            delete user.password;
-            done(null, user);
-        });
-});
-
-//This function gets called when trying to log in, to make the code more efficient and not using repetitive code
-var loginCallbackHandler = function (objectMapper, type) {
-    return function (arg1, arg2, arg3, arg4) {
-        /*
-        * If the type is github
-        * arg1 is the accessToken
-        * arg2 is the refresh token
-        * arg3 is profile object
-        * arg4 is done callback
-        *
-        * if the type is local
-        * arg1 is username
-        * arg2 is password
-        * arg3 is done callback
-        * */
-        var username, done;
-        if (type === "github") {
-            username = arg3.username;
-            done = arg4;
-        } else {
-            username = arg1;
-            done = arg3;
-        }
-
-        //Arg1 is the accessToken when using GitHub, so we are checking if it's not null to make sure everything is fine
-        if (arg1 !== null) {
-            r
-                .table('users')
-                .getAll(username.toLowerCase(), { index: 'usernameL' })
-                .run(r.conn)
-                .then(function (cursor) {
-                    return cursor.toArray()
-                        .then(function (users) {
-                            if (users.length > 0) {
-                                var userType = users[0].type;
-                                if (userType === "github" && type === userType) {
-                                    return done(null, users[0]);
-                                } else if (userType === "local" && userType === type) {
-                                    var hash = users[0].password;
-                                    console.log("Checking password...");
-                                    //This compares the user hash with the password put in
-                                    bcrypt.compare(arg2, hash, function(err, isMatch) {
-                                        if (err || isMatch === false) {
-                                            //Incorrect password/error occured
-                                            //TODO Throw error
-                                        } else {
-                                            var user = users[0];
-                                            delete user.password;
-                                            return done(null, user);
-                                        }
-                                    });
-                                } else {
-                                    // If this gets called, a user tried to sign in with a GitHub account even though a normal account with that username already exists, or the other way around
-                                    if (userType === "local") {
-                                        return done(null, false, {message: "This account has been registered via email, not via GitHub."});
-                                    } else {
-                                        return done(null, false, {message: "This account has been registered via GitHub, not via email."});
-                                    }
-                                }
-                            } else if (type === "github") {
-                                //TODO Check if this allows you to have duplicate emails/usernames
-                                //This gets called to create an account with GitHub if none exist yet
-                                return r.table('users')
-                                    .insert(objectMapper(arg3))
-                                    .run(r.conn)
-                                    .then(function (response) {
-                                        return r.table('users')
-                                            .get(response.generated_keys[0])
-                                            .run(r.conn);
-                                    })
-                                    .then(function (newUser) {
-                                        done(null, newUser);
-                                    });
-                            } else {
-                                console.log("User not found");
-                                done(null, false, {message: 'Account not found.'});
-                                // TODO Say account not found
-                            }
-                        });
-                })
-                .catch(function (err) {
-                    console.log('Error Getting User', err);
-                });
-        }
-    };
-};
-//This is the callback url which gets used with the GitHub authentication
-//TODO Make this config dependent so it's not hardcoded
-var callbackURL = 'http://127.0.0.1:3000/auth/login/callback';
-
-//Github strategy
-passport.use(new GitHubStrategy({
-        clientID: "c5516f218aa8682ac67d",
-        clientSecret: "5a3ee482ab2eb4ade56ab6ea01fd7544dd9a9be9",//TODO Make this secret
-        callbackURL: callbackURL + '/github'
-    },
-    loginCallbackHandler(function (profile) {
-        //The object that gets created with the GitHub API response, which will be inserted into the users table
-        return {
-            'username': profile.username,
-            'usernameL': profile.username.toLowerCase(),
-            'name': profile.displayName || undefined,
-            'email': 'email',
-            'type': 'github'
-        };
-    }, 'github')
-));
-
-//Local strategy
-passport.use(new LocalStrategy(
-    {},
-    loginCallbackHandler(undefined, 'local')
-));
-
-//Function to check if user is logged in
-passport.checkIfLoggedIn = function (req, res, next) {
-    if (req.user) {
-        return next();
-    }
-    return res.status(401).send('You\'re not logged in');
-};
-
-module.exports = passport;

+ 0 - 27
server/db/index.js

@@ -1,27 +0,0 @@
-var r = require('rethinkdb');
-require('rethinkdb-init')(r);
-
-r.connections = [];
-
-//Creates new connection
-r.getNewConnection = function () {
-    return r.connect({host: 'localhost', port: 28015, db: 'musare'}).then(function (conn) {
-        conn.use("musare");
-        r.connections.push(conn);
-        return conn;
-    });
-};
-
-//Sets up the tables for the database
-r.init({host: 'localhost', port: 28015, db: 'musare'}, [
-    {
-        name: 'users',
-        indexes: ['username', 'usernameL', 'email']
-    }
-]).then(function (conn) {
-    r.conn = conn;
-    r.connections.push(conn);
-    r.conn.use("musare");
-});
-
-module.exports = r;

+ 0 - 3
server/global.js

@@ -1,3 +0,0 @@
-/**
- * Created by KrisVos130 on 28/07/2016.
- */

+ 0 - 0
server/routes.js


+ 0 - 68
server/server.js

@@ -1,68 +0,0 @@
-/*var express = require('express');
-var app = express();
-
-app.get('/', function (req, res) {
-    res.sendFile('client/html/index.html', {root: "./"})
-});
-
-app.use(express.static('static'));
-
-app.listen(3000, function () {
-    console.log('Example app listening on port 3000!');
-});*/
-
-
-
-/* Database stuff
-    var r = require('rethinkdb');
-    var connection = null;
-    r.connect( {host: 'localhost', port: 28015, db: "musare"}, function(err, conn) {
-        if (err) throw err;
-        connection = conn;
-    });
-*/
-
-var express = require('express');
-var expressValidator = require('express-validator');
-var session = require('express-session');
-var engines = require('consolidate');
-
-var app = express();
-var auth = require('./auth');
-var authRouter = require('./auth/auth-router');
-
-// Middleware
-app
-    .use(session({
-        secret: 'thisisoursecretcode',
-        resave: false,
-        saveUninitialized: true
-    }))
-    .use(auth.initialize())
-    .use(auth.session())
-    .use(expressValidator({}));
-
-// Views
-app
-    .set('views', './client/html')
-    .engine('html', engines.mustache)
-    .set('view engine', 'html');
-
-// Routes
-app
-    .use('/auth', authRouter)
-    .get('/', function (req, res) {
-        res.render('index.html', { user: req.user });
-    })
-    .get('/login', function (req, res) {
-        res.render('login.html');
-    })
-    .get('/register', function (req, res) {
-        res.render('register.html');
-    })
-    .use(express.static(__dirname + '../static'))
-    .use('*', function (req, res) {
-        res.status(404).send('404 Not Found').end();
-    });
-
-app.listen(3000);

+ 0 - 1
static/test.txt

@@ -1 +0,0 @@
-Hey!