Browse Source

feat: added way to automatically fetch new SoundCloud API key

Kristian Vos 2 years ago
parent
commit
c5dc53e3d9
2 changed files with 161 additions and 10 deletions
  1. 70 0
      backend/logic/cache/index.js
  2. 91 10
      backend/logic/soundcloud.js

+ 70 - 0
backend/logic/cache/index.js

@@ -113,6 +113,40 @@ class _CacheModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Sets a single value
+	 *
+	 * @param {object} payload - object containing payload
+	 * @param {string} payload.key -  name of the key to set
+	 * @param {*} payload.value - the value we want to set
+	 * @param {boolean} [payload.stringifyJson=true] - stringify 'value' if it's an Object or Array
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
+	SET(payload) {
+		return new Promise((resolve, reject) => {
+			let { key } = payload;
+			let { value } = payload;
+
+			if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();
+			// automatically stringify objects and arrays into JSON
+			if (["object", "array"].includes(typeof value)) value = JSON.stringify(value);
+
+			CacheModule.client
+				.SET(key, value)
+				.then(() => {
+					let parsed = value;
+					try {
+						parsed = JSON.parse(value);
+					} catch {
+						// Do nothing
+					}
+
+					resolve(parsed);
+				})
+				.catch(err => reject(new Error(err)));
+		});
+	}
+
 	/**
 	 * Sets a single value in a table
 	 *
@@ -148,6 +182,42 @@ class _CacheModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Gets a single value
+	 *
+	 * @param {object} payload - object containing payload
+	 * @param {string} payload.key - name of the key to fetch
+	 * @param {boolean} [payload.parseJson=true] - attempt to parse returned data as JSON
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
+	GET(payload) {
+		return new Promise((resolve, reject) => {
+			let { key } = payload;
+
+			if (!key) {
+				reject(new Error("Invalid key!"));
+				return;
+			}
+			if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();
+
+			CacheModule.client
+				.GET(key, payload.value)
+				.then(value => {
+					if (value && !value.startsWith("{") && !value.startsWith("[")) return resolve(value);
+
+					let parsedValue;
+					try {
+						parsedValue = JSON.parse(value);
+					} catch (err) {
+						return reject(err);
+					}
+
+					return resolve(parsedValue);
+				})
+				.catch(err => reject(new Error(err)));
+		});
+	}
+
 	/**
 	 * Gets a single value from a table
 	 *

+ 91 - 10
backend/logic/soundcloud.js

@@ -1,6 +1,7 @@
 import mongoose from "mongoose";
 import async from "async";
 import config from "config";
+import sckey from "soundcloud-key-fetch";
 
 import * as rax from "retry-axios";
 import axios from "axios";
@@ -9,6 +10,7 @@ import CoreClass from "../core";
 
 let SoundCloudModule;
 let DBModule;
+let CacheModule;
 let MediaModule;
 
 const soundcloudTrackObjectToMusareTrackObject = soundcloudTrackObject => {
@@ -98,6 +100,7 @@ class _SoundCloudModule extends CoreClass {
 	 */
 	async initialize() {
 		DBModule = this.moduleManager.modules.db;
+		CacheModule = this.moduleManager.modules.cache;
 		MediaModule = this.moduleManager.modules.media;
 
 		// this.youtubeApiRequestModel = this.YoutubeApiRequestModel = await DBModule.runJob("GET_MODEL", {
@@ -108,7 +111,7 @@ class _SoundCloudModule extends CoreClass {
 			modelName: "soundcloudTrack"
 		});
 
-		return new Promise(resolve => {
+		return new Promise((resolve, reject) => {
 			this.rateLimiter = new RateLimitter(config.get("apis.soundcloud.rateLimit"));
 			this.requestTimeout = config.get("apis.soundcloud.requestTimeout");
 
@@ -120,15 +123,91 @@ class _SoundCloudModule extends CoreClass {
 			};
 			rax.attach(this.axios);
 
-			// SoundCloudModule.runJob("GET_TRACK", { identifier: 469902882, createMissing: false })
-			// 	.then(res => {
-			// 		console.log(57567, res);
-			// 	})
-			// 	.catch(err => {
-			// 		console.log(78768, err);
-			// 	});
+			this.apiKey = null;
+
+			SoundCloudModule.runJob("TEST_SOUNDCLOUD_API_KEY", {}, null, -1)
+				.then(result => {
+					if (result) {
+						resolve();
+						return;
+					}
 
-			resolve();
+					SoundCloudModule.runJob("GENERATE_SOUNDCLOUD_API_KEY", {}, null, -1)
+						.then(() => {
+							resolve();
+						})
+						.catch(reject);
+				})
+				.catch(reject);
+		});
+	}
+
+	/**
+	 *
+	 * @returns
+	 */
+	GENERATE_SOUNDCLOUD_API_KEY() {
+		return new Promise((resolve, reject) => {
+			this.log("INFO", "Fetching new SoundCloud API key.");
+			sckey
+				.fetchKey()
+				.then(soundcloudApiKey => {
+					if (!soundcloudApiKey) {
+						this.log("ERROR", "Couldn't fetch new SoundCloud API key.");
+						reject(new Error("Couldn't fetch SoundCloud key."));
+						return;
+					}
+
+					SoundCloudModule.soundcloudApiKey = soundcloudApiKey;
+
+					CacheModule.runJob("SET", { key: "soundcloudApiKey", value: soundcloudApiKey }, this)
+						.then(() => {
+							SoundCloudModule.runJob("TEST_SOUNDCLOUD_API_KEY", {}, this).then(result => {
+								if (!result) {
+									this.log("ERROR", "Fetched SoundCloud API key is invalid.");
+									reject(new Error("SoundCloud key isn't valid."));
+								} else {
+									this.log("INFO", "Fetched new valid SoundCloud API key.");
+									resolve();
+								}
+							});
+						})
+						.catch(err => {
+							this.log("ERROR", `Couldn't set new SoundCloud API key in cache. Error: ${err.message}`);
+							reject(err);
+						});
+				})
+				.catch(err => {
+					this.log("ERROR", `Couldn't fetch new SoundCloud API key. Error: ${err.message}`);
+					reject(new Error("Couldn't fetch SoundCloud key."));
+				});
+		});
+	}
+
+	/**
+	 *
+	 * @returns
+	 */
+	TEST_SOUNDCLOUD_API_KEY() {
+		return new Promise((resolve, reject) => {
+			this.log("INFO", "Testing SoundCloud API key.");
+			CacheModule.runJob("GET", { key: "soundcloudApiKey" }, this).then(soundcloudApiKey => {
+				if (!soundcloudApiKey) {
+					this.log("ERROR", "No SoundCloud API key found in cache.");
+					return resolve(false);
+				}
+
+				sckey
+					.testKey(soundcloudApiKey)
+					.then(res => {
+						this.log("INFO", `Tested SoundCloud API key. Result: ${res}`);
+						resolve(res);
+					})
+					.catch(err => {
+						this.log("ERROR", `Testing SoundCloud API key error: ${err.message}`);
+						reject(err);
+					});
+			});
 		});
 	}
 
@@ -173,8 +252,10 @@ class _SoundCloudModule extends CoreClass {
 			// const { url, params, quotaCost } = payload;
 			const { url } = payload;
 
+			const { soundcloudApiKey } = SoundCloudModule;
+
 			const params = {
-				client_id: config.get("apis.soundcloud.key")
+				client_id: soundcloudApiKey
 			};
 
 			SoundCloudModule.axios