UNPKG

audio-source-composer

Version:

Audio Source Composer

244 lines (203 loc) 8.11 kB
import bcrypt from "bcrypt"; import path from "path"; import fs from "fs"; export default class ServerUser { constructor(email, server) { if(!email) throw new Error("Invalid user email"); if(email.indexOf('@') === -1) throw new Error("Invalid user email: " + email); if(!server) throw new Error("Invalid server instance"); this.server = server; this.email = email; this.private = null; } readPrivateData() { if(this.private) return this.private; return this.private = readJSONFile(this.getPrivateUserJSONPath()); } getUsername() { const {username} = this.readPrivateData(); if(!username) throw new Error("User has no username."); return username; } async isRegistered() { return fs.existsSync(this.getPrivateUserJSONPath()); } /** Actions **/ async register(password=null, username=null, artistTitle=null) { if(await this.isRegistered()) throw new Error("User is already registered: " + this.email); // const userFile = path.resolve(this.getPrivateUserDirectory(), FILE_USER); if(!username) username = this.email.split('@')[0]; if(!artistTitle) artistTitle = username; for(const user of ServerUser.eachUser(this.server)) { const {userName:existingUsername} = readJSONFile(user.getPrivateUserJSONPath()); if(username === existingUsername) throw new Error("Artist name already exists: " + username); } const privateJSON = { username }; if(password) { privateJSON.password = await ServerUser.hash(password); } const artistJSON = { title: artistTitle }; writeFile(this.getPrivateUserJSONPath(), JSON.stringify(privateJSON)); writeFile(this.getPublicArtistJSONPath(), JSON.stringify(artistJSON)); console.log("Registered User:", this.email); } async unregister() { if(!await this.isRegistered()) throw new Error("User is not registered: " + this.email); console.log("Unregistering User:", this.email); this.erasePrivateUserDirectory(); } async login(password, session) { if(!session) throw new Error("Invalid session object"); if(!password) throw new Error("Invalid password"); if(!await this.isRegistered()) throw new Error("User is not registered: " + this.email); const userJSON = this.readPrivateData(); // console.log('userJSON', userJSON); if(!userJSON.password) throw new Error("This account has no password. Email login is required"); if(!await ServerUser.compare(password, userJSON.password)) throw new Error("Invalid password"); session.loggedIn = true; session.email = this.email; console.log("Login Successful:", session); } logout(session) { const email = session.email || "[No Email]"; session.destroy(); console.log("Logout Successful:", email); } /** Static **/ static hasSession(session) { return (session && session.loggedIn) } static getSession(server, session) { if(!this.hasSession(session)) throw new Error("Invalid user session. Please log in"); if(!session.email) throw new Error("Invalid session email. Please re-log in"); return new ServerUser(session.email, server); } static async hash(password, saltRounds=10) { return new Promise((resolve, reject) => { bcrypt.hash(password, saltRounds, (err, hash) => { if(err) reject(err); resolve(hash); }) }) } static async compare(password, hash) { return new Promise((resolve, reject) => { bcrypt.compare(password, hash, (err, response) => { if(err) reject(err); resolve(response); }) }) } /** * * @param {Server} server * @returns {Generator<ServerUser, void, *>} */ static * eachUser(server) { const privateUsersDirectory = server.getPrivatePath(PATH_USERS_PRIVATE); if(fs.existsSync(privateUsersDirectory)) { const domains = fs.readdirSync(privateUsersDirectory); for (const domain of domains) { if (domain) { const privateUserDomainDirectory = path.resolve(privateUsersDirectory, domain); const users = fs.readdirSync(privateUserDomainDirectory); for (const user of users) { yield new ServerUser(user + '@' + domain, server); } } } } } /** Paths **/ static getPublicUsersPath(server, ...paths) { return server.getPublicPath(PATH_USERS_PUBLIC, ...paths); } static getPublicUserSongPath(server, username, ...paths) { return server.getPublicPath(PATH_USERS_PUBLIC, username, PATH_USER_SONG, ...paths); } getPublicUserPath(...paths) { return this.server.getPublicPath(PATH_USERS_PUBLIC, this.getUsername(), ...paths); } getPublicUserSongPath(...paths) { return this.getPublicUserPath(PATH_USER_SONG, ...paths); } getPublicArtistJSONPath() { return this.getPublicUserPath(FILE_PUBLIC_ARTIST); } getPrivateUserPath(...paths) { const [emailUser,domain] = this.email.split('@'); return this.server.getPrivatePath(PATH_USERS_PRIVATE, domain, emailUser, ...paths); } getPrivateUserJSONPath() { return this.getPrivateUserPath(FILE_PRIVATE_USER); } erasePrivateUserDirectory() { const userPath = this.getPrivateUserPath(); console.log("Erasing ", userPath); rimraf(userPath); } getPublicUserURL(...paths) { return this.server.getPublicURL(PATH_USERS_PUBLIC, this.getUsername(), ...paths); } getPublicUserSongURL(...paths) { return this.getPublicUserURL(PATH_USER_SONG, ...paths); } } const PATH_USERS_PUBLIC = 'user'; const PATH_USERS_PRIVATE = 'user'; const PATH_USER_SONG = 'song'; const FILE_PRIVATE_USER = 'user.json'; const FILE_PUBLIC_ARTIST = 'artist.json'; /** * Remove directory recursively * @param {string} dir_path * @see https://stackoverflow.com/a/42505874/3027390 */ function rimraf(dir_path) { if (fs.existsSync(dir_path)) { fs.readdirSync(dir_path).forEach(function(entry) { var entry_path = path.join(dir_path, entry); if (fs.lstatSync(entry_path).isDirectory()) { rimraf(entry_path); } else { fs.unlinkSync(entry_path); } }); fs.rmdirSync(dir_path); } } function readJSONFile(userFile) { const content = fs.readFileSync(userFile, 'utf8'); // console.log("Reading File: ", userFile, content); return JSON.parse(content); } function writeFile(filePath, content) { const directory = path.resolve(filePath, '..'); // console.log("Writing Directory: ", directory) fs.mkdirSync(directory, { recursive: true }); // console.log("Writing File: ", filePath) fs.writeFileSync(filePath, content); return filePath; } // /** Test **/ // setTimeout(async () => { // const serverUser = new ServerUser('test@email.com'); // // const serverUser2 = new ServerUser('test2@email.com'); // try { await serverUser.register('test', 'test') } // catch (e) { console.error(e); } // try { await serverUser.unregister() } // catch (e) { console.error(e); } // // // try { await serverUser2.register('test', 'test') } // // catch (e) { console.error(e); } // // try { await serverUser2.unregister() } // // catch (e) { console.error(e); } // // await serverUser.login('test') // }, // 100 // );