Browse Source

Worked on replacing station _id's with station name's.

KrisVos130 8 years ago
parent
commit
6cff7ab31e

+ 52 - 21
backend/logic/actions/stations.js

@@ -137,16 +137,16 @@ module.exports = {
 	},
 
 	/**
-	 * Finds a station by id
+	 * Finds a station by name
 	 *
 	 * @param session
-	 * @param stationId - the station id
+	 * @param stationName - the station name
 	 * @param cb
 	 */
-	find: (session, stationId, cb) => {
+	findByName: (session, stationName, cb) => {
 		async.waterfall([
 			(next) => {
-				stations.getStation(stationId, next);
+				stations.getStationByName(stationName, next);
 			},
 
 			(station, next) => {
@@ -156,10 +156,10 @@ module.exports = {
 		], (err, station) => {
 			if (err) {
 				err = utils.getError(err);
-				logger.error("STATIONS_FIND", `Finding station "${stationId}" failed. "${err}"`);
+				logger.error("STATIONS_FIND_BY_NAME", `Finding station "${stationName}" failed. "${err}"`);
 				return cb({'status': 'failure', 'message': err});
 			}
-			logger.success("STATIONS_FIND", `Found station "${stationId}" successfully.`);
+			logger.success("STATIONS_FIND_BY_NAME", `Found station "${stationName}" successfully.`);
 			cb({status: 'success', data: station});
 		});
 	},
@@ -203,17 +203,17 @@ module.exports = {
 	},
 
 	/**
-	 * Joins the station by its id
+	 * Joins the station by its name
 	 *
 	 * @param session
-	 * @param stationId - the station id
+	 * @param stationName - the station name
 	 * @param cb
 	 * @return {{ status: String, userCount: Integer }}
 	 */
-	join: (session, stationId, cb) => {
+	join: (session, stationName, cb) => {
 		async.waterfall([
 			(next) => {
-				stations.getStation(stationId, next);
+				stations.getStationByName(stationName, next);
 			},
 
 			(station, next) => {
@@ -243,8 +243,9 @@ module.exports = {
 			},
 
 			(station, next) => {
-				utils.socketJoinRoom(session.socketId, `station.${stationId}`);
+				utils.socketJoinRoom(session.socketId, `station.${station._id}`);
 				let data = {
+					_id: station._id,
 					type: station.type,
 					currentSong: station.currentSong,
 					startedAt: station.startedAt,
@@ -261,7 +262,7 @@ module.exports = {
 			},
 
 			(data, next) => {
-				if (!data.currentSong) return next(null, data);
+				if (!data.currentSong || !data.currentSong.title) return next(null, data);
 				utils.socketJoinSongRoom(session.socketId, `song.${data.currentSong.songId}`);
 				data.currentSong.skipVotes = data.currentSong.skipVotes.length;
 				songs.getSong(data.currentSong.songId, (err, song) => {
@@ -278,10 +279,10 @@ module.exports = {
 		], (err, data) => {
 			if (err) {
 				err = utils.getError(err);
-				logger.error("STATIONS_JOIN", `Joining station "${stationId}" failed. "${err}"`);
+				logger.error("STATIONS_JOIN", `Joining station "${stationName}" failed. "${err}"`);
 				return cb({'status': 'failure', 'message': err});
 			}
-			logger.success("STATIONS_JOIN", `Joined station "${stationId}" successfully.`);
+			logger.success("STATIONS_JOIN", `Joined station "${data._id}" successfully.`);
 			cb({status: 'success', data});
 		});
 	},
@@ -396,6 +397,34 @@ module.exports = {
 		});
 	},
 
+	/**
+	 * Updates a station's name
+	 *
+	 * @param session
+	 * @param stationId - the station id
+	 * @param newName - the new station name
+	 * @param cb
+	 */
+	updateName: hooks.ownerRequired((session, stationId, newName, cb) => {
+		async.waterfall([
+			(next) => {
+				db.models.station.update({_id: stationId}, {$set: {name: newName}}, next);
+			},
+
+			(res, next) => {
+				stations.updateStation(stationId, next);
+			}
+		], (err) => {
+			if (err) {
+				err = utils.getError(err);
+				logger.error("STATIONS_UPDATE_DISPLAY_NAME", `Updating station "${stationId}" displayName to "${newName}" failed. "${err}"`);
+				return cb({'status': 'failure', 'message': err});
+			}
+			logger.success("STATIONS_UPDATE_DISPLAY_NAME", `Updated station "${stationId}" displayName to "${newName}" successfully.`);
+			return cb({'status': 'success', 'message': 'Successfully updated the name.'});
+		});
+	}),
+
 	/**
 	 * Updates a station's display name
 	 *
@@ -615,7 +644,7 @@ module.exports = {
 	}),
 
 	/**
-	 * Created a station
+	 * Create a station
 	 *
 	 * @param session
 	 * @param data - the station data
@@ -623,7 +652,8 @@ module.exports = {
 	 * @param userId
 	 */
 	create: hooks.loginRequired((session, data, cb, userId) => {
-		data._id = data._id.toLowerCase();
+		console.log(data);
+		data.name = data.name.toLowerCase();
 		let blacklist = ["country", "edm", "musare", "hip-hop", "rap", "top-hits", "todays-hits", "old-school", "christmas", "about", "support", "staff", "help", "news", "terms", "privacy", "profile", "c", "community", "tos", "login", "register", "p", "official", "o", "trap", "faq", "team", "donate", "buy", "shop", "forums", "explore", "settings", "admin", "auth", "reset_password"];
 		async.waterfall([
 			(next) => {
@@ -632,19 +662,19 @@ module.exports = {
 			},
 
 			(next) => {
-				db.models.station.findOne({ $or: [{_id: data._id}, {displayName: new RegExp(`^${data.displayName}$`, 'i')}] }, next);
+				db.models.station.findOne({ $or: [{name: data.name}, {displayName: new RegExp(`^${data.displayName}$`, 'i')}] }, next);
 			},
 
 			(station, next) => {
 				if (station) return next('A station with that name or display name already exists.');
-				const { _id, displayName, description, genres, playlist, type, blacklistedGenres } = data;
+				const { name, displayName, description, genres, playlist, type, blacklistedGenres } = data;
 				if (type === 'official') {
 					db.models.user.findOne({_id: userId}, (err, user) => {
 						if (err) return next(err);
 						if (!user) return next('User not found.');
 						if (user.role !== 'admin') return next('Admin required.');
 						db.models.station.create({
-							_id,
+							name,
 							displayName,
 							description,
 							type,
@@ -656,9 +686,9 @@ module.exports = {
 						}, next);
 					});
 				} else if (type === 'community') {
-					if (blacklist.indexOf(_id) !== -1) return next('That id is blacklisted. Please use a different id.');
+					if (blacklist.indexOf(name) !== -1) return next('That name is blacklisted. Please use a different name.');
 					db.models.station.create({
-						_id,
+						name,
 						displayName,
 						description,
 						type,
@@ -671,6 +701,7 @@ module.exports = {
 			}
 		], (err, station) => {
 			if (err) {
+				console.log(err);
 				err = utils.getError(err);
 				logger.error("STATIONS_CREATE", `Creating station failed. "${err}"`);
 				return cb({'status': 'failure', 'message': err});

+ 4 - 1
backend/logic/cache/index.js

@@ -1,6 +1,7 @@
 'use strict';
 
 const redis = require('redis');
+const mongoose = require('mongoose');
 
 // Lightweight / convenience wrapper around redis module for our needs
 
@@ -62,7 +63,7 @@ const lib = {
 	 * @param {Boolean} [stringifyJson=true] - stringify 'value' if it's an Object or Array
 	 */
 	hset: (table, key, value, cb, stringifyJson = true) => {
-
+		if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();
 		// automatically stringify objects and arrays into JSON
 		if (stringifyJson && ['object', 'array'].includes(typeof value)) value = JSON.stringify(value);
 
@@ -84,6 +85,7 @@ const lib = {
 	 */
 	hget: (table, key, cb, parseJson = true) => {
 		if (!key || !table) return typeof cb === 'function' ? cb(null, null) : null;
+		if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();
 		lib.client.hget(table, key, (err, value) => {
 			if (err) return typeof cb === 'function' ? cb(err) : null;
 			if (parseJson) try {
@@ -103,6 +105,7 @@ const lib = {
 	 */
 	hdel: (table, key, cb) => {
 		if (!key || !table) return cb(null, null);
+		if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();
 		lib.client.hdel(table, key, (err) => {
 			if (err) return cb(err);
 			else return cb(null);

+ 1 - 1
backend/logic/db/index.js

@@ -33,7 +33,7 @@ let lib = {
 				report: new mongoose.Schema(require(`./schemas/report`))
 			};
 
-			lib.schemas.station.path('_id').validate((id) => {
+			lib.schemas.station.path('name').validate((id) => {
 				return /^[a-z]+$/.test(id);
 			}, 'The id can only have the letters a-z.');
 

+ 1 - 1
backend/logic/db/schemas/station.js

@@ -1,5 +1,5 @@
 module.exports = {
-	_id: { type: String, lowercase: true, maxlength: 16, minlength: 2, index: true, unique: true, required: true },
+	name: { type: String, lowercase: true, maxlength: 16, minlength: 2, index: true, unique: true, required: true },
 	type: { type: String, enum: ["official", "community"], required: true },
 	displayName: { type: String, minlength: 2, maxlength: 32, required: true, unique: true },
 	description: { type: String, minlength: 2, maxlength: 128, required: true },

+ 26 - 0
backend/logic/stations.js

@@ -210,6 +210,32 @@ module.exports = {
 		});
 	},
 
+	// Attempts to get the station from Redis. If it's not in Redis, get it from Mongo and add it to Redis.
+	getStationByName: function(stationName, cb) {
+		let _this = this;
+		async.waterfall([
+
+			(next) => {
+				db.models.station.findOne({name: stationName}, next);
+			},
+
+			(station, next) => {
+				if (station) {
+					if (station.type === 'official') {
+						_this.calculateOfficialPlaylistList(station._id, station.playlist, ()=>{});
+					}
+					station = cache.schemas.station(station);
+					cache.hset('stations', station._id, station);
+					next(true, station);
+				} else next('Station not found');
+			},
+
+		], (err, station) => {
+			if (err && err !== true) cb(err);
+			cb(null, station);
+		});
+	},
+
 	updateStation: function(stationId, cb) {
 		let _this = this;
 		async.waterfall([

+ 13 - 8
frontend/components/Admin/Stations.vue

@@ -4,6 +4,7 @@
 			<thead>
 				<tr>
 					<td>ID</td>
+					<td>Name</td>
 					<td>Type</td>
 					<td>Display Name</td>
 					<td>Description</td>
@@ -13,16 +14,19 @@
 			<tbody>
 				<tr v-for='(index, station) in stations' track-by='$index'>
 					<td>
-						<span>{{ station._id }}</span>
+						<span>{{station._id}}</span>
 					</td>
 					<td>
-						<span>{{ station.type }}</span>
+						<span>{{station.name}}</span>
 					</td>
 					<td>
-						<span>{{ station.displayName }}</span>
+						<span>{{station.type}}</span>
 					</td>
 					<td>
-						<span>{{ station.description }}</span>
+						<span>{{station.displayName}}</span>
+					</td>
+					<td>
+						<span>{{station.description}}</span>
 					</td>
 					<td>
 						<a class='button is-info' @click='editStation(station)'>Edit</a>
@@ -42,7 +46,7 @@
 					<div class='control is-horizontal'>
 						<div class='control is-grouped'>
 							<p class='control is-expanded'>
-								<input class='input' type='text' placeholder='Unique Identifier' v-model='newStation._id'>
+								<input class='input' type='text' placeholder='Name' v-model='newStation.name'>
 							</p>
 							<p class='control is-expanded'>
 								<input class='input' type='text' placeholder='Display Name' v-model='newStation.displayName'>
@@ -110,14 +114,14 @@
 			},
 			createStation: function () {
 				let _this = this;
-				let { newStation: { _id, displayName, description, genres, blacklistedGenres } } = this;
+				let {newStation: {name, displayName, description, genres, blacklistedGenres}} = this;
 
-				if (_id == undefined) return Toast.methods.addToast('Field (YouTube ID) cannot be empty', 3000);
+				if (name == undefined) return Toast.methods.addToast('Field (Name) cannot be empty', 3000);
 				if (displayName == undefined) return Toast.methods.addToast('Field (Display Name) cannot be empty', 3000);
 				if (description == undefined) return Toast.methods.addToast('Field (Description) cannot be empty', 3000);
 
 				_this.socket.emit('stations.create', {
-					_id,
+					name,
 					type: 'official',
 					displayName,
 					description,
@@ -139,6 +143,7 @@
 			editStation: function (station) {
 				this.$broadcast('editStation', {
 					_id: station._id,
+					name: station.name,
 					type: station.type,
 					partyMode: station.partyMode,
 					description: station.description,

+ 5 - 5
frontend/components/Modals/CreateCommunityStation.vue

@@ -2,9 +2,9 @@
 	<modal title='Create Community Station'>
 		<div slot='body'>
 			<!-- validation to check if exists http://bulma.io/documentation/elements/form/ -->
-			<label class='label'>Unique ID (lowercase, a-z, used in the url)</label>
+			<label class='label'>Name (lowercase, a-z, used in the url)</label>
 			<p class='control'>
-				<input class='input' type='text' placeholder='Name...' v-model='newCommunity._id' autofocus>
+				<input class='input' type='text' placeholder='Name...' v-model='newCommunity.name' autofocus>
 			</p>
 			<label class='label'>Display Name</label>
 			<p class='control'>
@@ -31,7 +31,7 @@
 		data() {
 			return {
 				newCommunity: {
-					_id: '',
+					name: '',
 					displayName: '',
 					description: ''
 				}
@@ -49,11 +49,11 @@
 			},
 			submitModal: function () {
 				let _this = this;
-				if (_this.newCommunity._id == '') return Toast.methods.addToast('ID cannot be a blank field', 3000);
+				if (_this.newCommunity.name == '') return Toast.methods.addToast('Name cannot be a blank field', 3000);
 				if (_this.newCommunity.displayName == '') return Toast.methods.addToast('Display Name cannot be a blank field', 3000);
 				if (_this.newCommunity.description == '') return Toast.methods.addToast('Description cannot be a blank field', 3000);
 				this.socket.emit('stations.create', {
-					_id: _this.newCommunity._id,
+					name: _this.newCommunity.name,
 					type: 'community',
 					displayName: _this.newCommunity.displayName,
 					description: _this.newCommunity.description

+ 25 - 1
frontend/components/Modals/EditStation.vue

@@ -2,10 +2,19 @@
 	<div>
 		<modal title='Edit Station'>
 			<div slot='body'>
+				<label class='label'>Name</label>
+				<div class='control is-grouped'>
+					<p class='control is-expanded'>
+						<input class='input' type='text' placeholder='Station Name' v-model='editing.name'>
+					</p>
+					<p class='control'>
+						<a class='button is-info' @click='updateName()' href='#'>Update</a>
+					</p>
+				</div>
 				<label class='label'>Display name</label>
 				<div class='control is-grouped'>
 					<p class='control is-expanded'>
-						<input class='input' type='text' placeholder='Station Display Name' v-model='editing.displayName' autofocus>
+						<input class='input' type='text' placeholder='Station Display Name' v-model='editing.displayName'>
 					</p>
 					<p class='control'>
 						<a class='button is-info' @click='updateDisplayName()' href='#'>Update</a>
@@ -62,6 +71,7 @@
 			return {
 				editing: {
 					_id: '',
+					name: '',
 					type: '',
 					displayName: '',
 					description: '',
@@ -71,6 +81,20 @@
 			}
 		},
 		methods: {
+			updateName: function () {
+				let _this = this;
+				this.socket.emit('stations.updateName', this.editing._id, this.editing.name, res => {
+					if (res.status === 'success') {
+						if (_this.$parent.station) _this.$parent.station.name = _this.editing.name;
+						else {
+							_this.$parent.stations.forEach((station, index) => {
+								if (station._id === _this.editing._id) return _this.$parent.stations[index].name = _this.editing.name;
+							});
+						}
+					}
+					Toast.methods.addToast(res.message, 8000);
+				});
+			},
 			updateDisplayName: function () {
 				let _this = this;
 				this.socket.emit('stations.updateDisplayName', this.editing._id, this.editing.displayName, res => {

+ 15 - 16
frontend/components/Station/Station.vue

@@ -16,7 +16,7 @@
 		<div v-show="noSong" class="no-song">
 			<h1>No song is currently playing</h1>
 			<h4 v-if='type === "community" && station.partyMode'>
-				<a href='#' class='no-song' @click='modals.addSongToQueue = true'>Add a Song to the Queue</a>
+				<a href='#' class='no-song' @click='modals.addSongToQueue = true'>Add a song to the queue</a>
 			</h4>
 			<h4 v-if='type === "community" && !station.partyMode && $parent.userId === station.owner && !station.privatePlaylist'>
 				<a href='#' class='no-song' @click='sidebars.playlist = true'>Play a private playlist</a>
@@ -137,7 +137,8 @@
 			editStation: function () {
 				let _this = this;
 				this.$broadcast('editStation', {
-					_id: _this.$route.params.id,
+					_id: _this.station._id,
+					name: _this.station.name,
 					type: _this.type,
 					partyMode: _this.station.partyMode,
 					description: _this.station.description,
@@ -262,28 +263,28 @@
 			},
 			skipStation: function () {
 				let _this = this;
-				_this.socket.emit('stations.forceSkip', _this.stationId, data => {
+				_this.socket.emit('stations.forceSkip', _this.station._id, data => {
 					if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
 					else Toast.methods.addToast('Successfully skipped the station\'s current song.', 4000);
 				});
 			},
 			voteSkipStation: function () {
 				let _this = this;
-				_this.socket.emit('stations.voteSkip', _this.stationId, data => {
+				_this.socket.emit('stations.voteSkip', _this.station._id, data => {
 					if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
 					else Toast.methods.addToast('Successfully voted to skip the current song.', 4000);
 				});
 			},
 			resumeStation: function () {
 				let _this = this;
-				_this.socket.emit('stations.resume', _this.stationId, data => {
+				_this.socket.emit('stations.resume', _this.station._id, data => {
 					if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
 					else Toast.methods.addToast('Successfully resumed the station.', 4000);
 				});
 			},
 			pauseStation: function () {
 				let _this = this;
-				_this.socket.emit('stations.pause', _this.stationId, data => {
+				_this.socket.emit('stations.pause', _this.station._id, data => {
 					if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
 					else Toast.methods.addToast('Successfully paused the station.', 4000);
 				});
@@ -346,7 +347,7 @@
 								console.log(data.song);
 								let songId = data.song._id;
 								_this.automaticallyRequestedSongId = data.song.songId;
-								_this.socket.emit('stations.addToQueue', _this.stationId, data.song.songId, data => {
+								_this.socket.emit('stations.addToQueue', _this.station._id, data.song.songId, data => {
 									if (data.status === 'success') {
 										_this.socket.emit('playlists.moveSongToBottom', _this.privatePlaylistQueueSelected, songId, data => {
 											if (data.status === 'success') {}
@@ -360,10 +361,11 @@
 			},
 			joinStation: function () {
 				let _this = this;
-				_this.socket.emit('stations.join', _this.stationId, res => {
+				_this.socket.emit('stations.join', _this.stationName, res => {
 					if (res.status === 'success') {
 						_this.station = {
-							_id: _this.stationId,
+							_id: res.data._id,
+							name: _this.stationName,
 							displayName: res.data.displayName,
 							description: res.data.description,
 							privacy: res.data.privacy,
@@ -395,12 +397,12 @@
 							_this.noSong = true;
 						}
 						if (_this.type === 'community') {
-							_this.socket.emit('stations.getQueue', _this.stationId, data => {
+							_this.socket.emit('stations.getQueue', _this.station._id, data => {
 								if (data.status === 'success') _this.songsList = data.queue;
 							});
 						}
 						if (_this.type === 'official') {
-							_this.socket.emit('stations.getPlaylist', _this.stationId, res => {
+							_this.socket.emit('stations.getPlaylist', _this.station._id, res => {
 								if (res.status == 'success') _this.songsList = res.data;
 							});
 						}
@@ -433,7 +435,7 @@
 				return new Date().getTime() + _this.systemDifference;
 			};
 
-			_this.stationId = _this.$route.params.id;
+			_this.stationName = _this.$route.params.id;
 
 			window.stationInterval = 0;
 
@@ -441,19 +443,16 @@
 				_this.socket = socket;
 
 				io.removeAllListeners();
-
 				if (_this.socket.connected) _this.joinStation();
 				io.onConnect(() => {
 					_this.joinStation();
 				});
-
-				_this.socket.emit('stations.find', _this.stationId, res => {
+				_this.socket.emit('stations.findByName', _this.stationName, res => {
 					if (res.status === 'error') {
 						_this.$router.go('/404');
 						Toast.methods.addToast(res.message, 3000);
 					}
 				});
-
 				_this.socket.on('event:songs.next', data => {
 					_this.previousSong = (_this.currentSong._id) ? _this.currentSong : null;
 					_this.currentSong = (data.currentSong) ? data.currentSong : {};

+ 4 - 4
frontend/components/pages/Home.vue

@@ -3,7 +3,7 @@
 		<main-header></main-header>
 		<div class="group">
 			<div class="group-title">Official Stations</div>
-			<div class="card station-card" v-for="station in stations.official" v-link="{ path: '/' + station._id }" @click="this.$dispatch('joinStation', station._id)">
+			<div class="card station-card" v-for="station in stations.official" v-link="{ path: '/' + station.name }" @click="this.$dispatch('joinStation', station._id)">
 				<div class="card-image">
 					<figure class="image is-square">
 						<img :src="station.currentSong.thumbnail" onerror="this.src='/assets/notes-transparent.png'" />
@@ -25,7 +25,7 @@
 						{{ station.description }}
 					</div>
 				</div>
-				<a @click="this.$dispatch('joinStation', station._id)" href='#' class='absolute-a' v-link="{ path: '/' + station._id }"></a>
+				<a @click="this.$dispatch('joinStation', station._id)" href='#' class='absolute-a' v-link="{ path: '/' + station.name }"></a>
 			</div>
 		</div>
 		<div class="group">
@@ -34,7 +34,7 @@
 				<a @click='modals.createCommunityStation = !modals.createCommunityStation' v-if="$parent.loggedIn" href='#'>
 				<i class="material-icons community-button">add_circle_outline</i></a>
 			</div>
-			<div class="card station-card" v-for="station in stations.community" v-link="{ path: '/community/' + station._id }" @click="this.$dispatch('joinStation', station._id)">
+			<div class="card station-card" v-for="station in stations.community" v-link="{ path: '/community/' + station.name }" @click="this.$dispatch('joinStation', station._id)">
 				<div class="card-image">
 					<figure class="image is-square">
 						<img :src="station.currentSong.thumbnail" onerror="this.src='/assets/notes-transparent.png'" />
@@ -59,7 +59,7 @@
 						{{ station.description }}
 					</div>
 				</div>
-				<a @click="this.$dispatch('joinStation', station._id)" href='#' class='absolute-a' v-link="{ path: '/community/' + station._id }"></a>
+				<a @click="this.$dispatch('joinStation', station._id)" href='#' class='absolute-a' v-link="{ path: '/community/' + station.name }"></a>
 			</div>
 		</div>
 		<main-footer></main-footer>