Browse Source

Switch config to .env

Pitu 6 years ago
parent
commit
89a271818e

+ 11 - 2
nuxt.config.js

@@ -1,11 +1,20 @@
+require('dotenv').config();
 import autoprefixer from 'autoprefixer';
 import autoprefixer from 'autoprefixer';
 import serveStatic from 'serve-static';
 import serveStatic from 'serve-static';
 import path from 'path';
 import path from 'path';
-import config from './config';
 
 
 export default {
 export default {
 	server: {
 	server: {
-		port: config.server.ports.frontend
+		port: process.env.WEBSITE_PORT
+	},
+	env: {
+		version: process.env.npm_package_version,
+		URL: process.env.DOMAIN,
+		baseURL: `${process.env.DOMAIN}${process.env.ROUTE_PREFIX}`,
+		serviceName: process.env.SERVICE_NAME,
+		maxFileSize: process.env.MAX_SIZE,
+		chunkSize: process.env.CHUNK_SIZE,
+		maxLinksPerAlbum: process.env.MAX_LINKS_PER_ALBUM
 	},
 	},
 	srcDir: 'src/site/',
 	srcDir: 'src/site/',
 	head: {
 	head: {

+ 1 - 0
package.json

@@ -36,6 +36,7 @@
 		"chalk": "^2.4.1",
 		"chalk": "^2.4.1",
 		"compression": "^1.7.2",
 		"compression": "^1.7.2",
 		"cors": "^2.8.4",
 		"cors": "^2.8.4",
+		"dotenv": "^6.2.0",
 		"dumper.js": "^1.1.1",
 		"dumper.js": "^1.1.1",
 		"express": "^4.16.3",
 		"express": "^4.16.3",
 		"express-rate-limit": "^2.11.0",
 		"express-rate-limit": "^2.11.0",

+ 1 - 3
src/api/routes/albums/albumDELETE.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
 const log = require('../../utils/Log');
 const log = require('../../utils/Log');
 
 
@@ -9,7 +7,7 @@ class albumDELETE extends Route {
 		super('/album/:id/:purge*?', 'delete');
 		super('/album/:id/:purge*?', 'delete');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		const { id, purge } = req.params;
 		const { id, purge } = req.params;
 		if (!id) return res.status(400).json({ message: 'Invalid album ID supplied' });
 		if (!id) return res.status(400).json({ message: 'Invalid album ID supplied' });
 
 

+ 1 - 3
src/api/routes/albums/albumGET.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
 
 
 class albumGET extends Route {
 class albumGET extends Route {
@@ -8,7 +6,7 @@ class albumGET extends Route {
 		super('/album/:identifier', 'get', { bypassAuth: true });
 		super('/album/:identifier', 'get', { bypassAuth: true });
 	}
 	}
 
 
-	async run(req, res) {
+	async run(req, res, db) {
 		const { identifier } = req.params;
 		const { identifier } = req.params;
 		if (!identifier) return res.status(400).json({ message: 'Invalid identifier supplied' });
 		if (!identifier) return res.status(400).json({ message: 'Invalid identifier supplied' });
 
 

+ 1 - 3
src/api/routes/albums/albumPOST.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const moment = require('moment');
 const moment = require('moment');
 
 
 class albumPOST extends Route {
 class albumPOST extends Route {
@@ -8,7 +6,7 @@ class albumPOST extends Route {
 		super('/album/new', 'post');
 		super('/album/new', 'post');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		const { name } = req.body;
 		const { name } = req.body;
 		if (!name) return res.status(400).json({ message: 'No name provided' });
 		if (!name) return res.status(400).json({ message: 'No name provided' });

+ 3 - 5
src/api/routes/albums/albumZipGET.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
 const log = require('../../utils/Log');
 const log = require('../../utils/Log');
 const path = require('path');
 const path = require('path');
@@ -11,7 +9,7 @@ class albumGET extends Route {
 		super('/album/:identifier/zip', 'get', { bypassAuth: true });
 		super('/album/:identifier/zip', 'get', { bypassAuth: true });
 	}
 	}
 
 
-	async run(req, res) {
+	async run(req, res, db) {
 		const { identifier } = req.params;
 		const { identifier } = req.params;
 		if (!identifier) return res.status(400).json({ message: 'Invalid identifier supplied' });
 		if (!identifier) return res.status(400).json({ message: 'Invalid identifier supplied' });
 
 
@@ -31,7 +29,7 @@ class albumGET extends Route {
 			If the date when the album was zipped is greater than the album's last edit, we just send the zip to the user
 			If the date when the album was zipped is greater than the album's last edit, we just send the zip to the user
 		*/
 		*/
 		if (album.zippedAt > album.editedAt) {
 		if (album.zippedAt > album.editedAt) {
-			const filePath = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder, 'zips', `${album.userId}-${album.id}.zip`);
+			const filePath = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER, 'zips', `${album.userId}-${album.id}.zip`);
 			const exists = await jetpack.existsAsync(filePath);
 			const exists = await jetpack.existsAsync(filePath);
 			/*
 			/*
 				Make sure the file exists just in case, and if not, continue to it's generation.
 				Make sure the file exists just in case, and if not, continue to it's generation.
@@ -65,7 +63,7 @@ class albumGET extends Route {
 			Util.createZip(filesToZip, album);
 			Util.createZip(filesToZip, album);
 			await db.table('albums').where('id', link.albumId).update('zippedAt', db.fn.now());
 			await db.table('albums').where('id', link.albumId).update('zippedAt', db.fn.now());
 
 
-			const filePath = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder, 'zips', `${album.userId}-${album.id}.zip`);
+			const filePath = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER, 'zips', `${album.userId}-${album.id}.zip`);
 			const fileName = `lolisafe-${identifier}.zip`;
 			const fileName = `lolisafe-${identifier}.zip`;
 			return res.download(filePath, fileName);
 			return res.download(filePath, fileName);
 		} catch (error) {
 		} catch (error) {

+ 2 - 4
src/api/routes/albums/albumsGET.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
 
 
 class albumsGET extends Route {
 class albumsGET extends Route {
@@ -8,7 +6,7 @@ class albumsGET extends Route {
 		super('/albums/mini', 'get');
 		super('/albums/mini', 'get');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		/*
 		/*
 			Let's fetch the albums. This route will only return a small portion
 			Let's fetch the albums. This route will only return a small portion
 			of the album files for displaying on the dashboard. It's probably useless
 			of the album files for displaying on the dashboard. It's probably useless
@@ -72,7 +70,7 @@ class albumsDropdownGET extends Route {
 		super('/albums/dropdown', 'get');
 		super('/albums/dropdown', 'get');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		const albums = await db.table('albums')
 		const albums = await db.table('albums')
 			.where('userId', user.id)
 			.where('userId', user.id)
 			.select('id', 'name');
 			.select('id', 'name');

+ 1 - 3
src/api/routes/albums/link/linkEditPOST.js

@@ -1,6 +1,4 @@
 const Route = require('../../../structures/Route');
 const Route = require('../../../structures/Route');
-const config = require('../../../../../config');
-const db = require('knex')(config.server.database);
 const log = require('../../../utils/Log');
 const log = require('../../../utils/Log');
 
 
 class linkEditPOST extends Route {
 class linkEditPOST extends Route {
@@ -8,7 +6,7 @@ class linkEditPOST extends Route {
 		super('/album/link/edit', 'post');
 		super('/album/link/edit', 'post');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		const { identifier, enabled, enableDownload, expiresAt } = req.body;
 		const { identifier, enabled, enableDownload, expiresAt } = req.body;
 		if (!identifier) return res.status(400).json({ message: 'Invalid album identifier supplied' });
 		if (!identifier) return res.status(400).json({ message: 'Invalid album identifier supplied' });

+ 2 - 4
src/api/routes/albums/link/linkPOST.js

@@ -1,6 +1,4 @@
 const Route = require('../../../structures/Route');
 const Route = require('../../../structures/Route');
-const config = require('../../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../../utils/Util');
 const Util = require('../../../utils/Util');
 const log = require('../../../utils/Log');
 const log = require('../../../utils/Log');
 
 
@@ -9,7 +7,7 @@ class linkPOST extends Route {
 		super('/album/link/new', 'post');
 		super('/album/link/new', 'post');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		const { albumId } = req.body;
 		const { albumId } = req.body;
 		if (!albumId) return res.status(400).json({ message: 'No album provided' });
 		if (!albumId) return res.status(400).json({ message: 'No album provided' });
@@ -24,7 +22,7 @@ class linkPOST extends Route {
 			Count the amount of links created for that album already and error out if max was reached
 			Count the amount of links created for that album already and error out if max was reached
 		*/
 		*/
 		const count = await db.table('links').where('albumId', albumId).count({ count: 'id' });
 		const count = await db.table('links').where('albumId', albumId).count({ count: 'id' });
-		if (count[0].count >= config.albums.maxLinksPerAlbum) return res.status(400).json({ message: 'Maximum links per album reached' });
+		if (count[0].count >= process.env.MAX_LINKS_PER_ALBUM) return res.status(400).json({ message: 'Maximum links per album reached' });
 
 
 		/*
 		/*
 			Try to allocate a new identifier on the db
 			Try to allocate a new identifier on the db

+ 1 - 3
src/api/routes/auth/changePasswordPOST.js

@@ -1,7 +1,5 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
 const log = require('../../utils/Log');
 const log = require('../../utils/Log');
-const db = require('knex')(config.server.database);
 const bcrypt = require('bcrypt');
 const bcrypt = require('bcrypt');
 const moment = require('moment');
 const moment = require('moment');
 
 
@@ -10,7 +8,7 @@ class changePasswordPOST extends Route {
 		super('/auth/password/change', 'post');
 		super('/auth/password/change', 'post');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		const { password, newPassword } = req.body;
 		const { password, newPassword } = req.body;
 		if (!password || !newPassword) return res.status(401).json({ message: 'Invalid body provided' });
 		if (!password || !newPassword) return res.status(401).json({ message: 'Invalid body provided' });

+ 2 - 4
src/api/routes/auth/registerPOST.js

@@ -1,7 +1,5 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
 const log = require('../../utils/Log');
 const log = require('../../utils/Log');
-const db = require('knex')(config.server.database);
 const bcrypt = require('bcrypt');
 const bcrypt = require('bcrypt');
 const randomstring = require('randomstring');
 const randomstring = require('randomstring');
 const moment = require('moment');
 const moment = require('moment');
@@ -11,8 +9,8 @@ class registerPOST extends Route {
 		super('/auth/register', 'post', { bypassAuth: true });
 		super('/auth/register', 'post', { bypassAuth: true });
 	}
 	}
 
 
-	async run(req, res) {
+	async run(req, res, db) {
-		if (!config.enableCreateUserAccounts) return res.status(401).json({ message: 'Creation of new accounts is currently disabled' });
+		if (!process.env.USER_ACCOUNTS) return res.status(401).json({ message: 'Creation of new accounts is currently disabled' });
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		if (!req.body) return res.status(400).json({ message: 'No body provided' });
 		const { username, password } = req.body;
 		const { username, password } = req.body;
 		if (!username || !password) return res.status(401).json({ message: 'Invalid body provided' });
 		if (!username || !password) return res.status(401).json({ message: 'Invalid body provided' });

+ 0 - 22
src/api/routes/configGET.js

@@ -1,22 +0,0 @@
-const Route = require('../structures/Route');
-const config = require('../../../config');
-
-class configGET extends Route {
-	constructor() {
-		super('/config', 'get', { bypassAuth: true });
-	}
-
-	run(req, res) {
-		return res.json({
-			version: process.env.npm_package_version,
-			URL: config.filesServeLocatio,
-			baseURL: config.backendLocation,
-			serviceName: config.serviceName,
-			maxFileSize: config.uploads.uploadMaxSize,
-			chunkSize: config.uploads.chunkSize,
-			maxLinksPerAlbum: config.albums.maxLinksPerAlbum
-		});
-	}
-}
-
-module.exports = configGET;

+ 1 - 3
src/api/routes/files/fileDELETE.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
 const log = require('../../utils/Log');
 const log = require('../../utils/Log');
 
 
@@ -9,7 +7,7 @@ class fileDELETE extends Route {
 		super('/file/:id', 'delete');
 		super('/file/:id', 'delete');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		const { id } = req.params;
 		const { id } = req.params;
 		if (!id) return res.status(400).json({ message: 'Invalid file ID supplied' });
 		if (!id) return res.status(400).json({ message: 'Invalid file ID supplied' });
 
 

+ 1 - 3
src/api/routes/files/filesGET.js

@@ -1,6 +1,4 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
-const db = require('knex')(config.server.database);
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
 
 
 class filesGET extends Route {
 class filesGET extends Route {
@@ -8,7 +6,7 @@ class filesGET extends Route {
 		super('/files', 'get');
 		super('/files', 'get');
 	}
 	}
 
 
-	async run(req, res, user) {
+	async run(req, res, db, user) {
 		/*
 		/*
 			Get all the files from the user
 			Get all the files from the user
 		*/
 		*/

+ 44 - 46
src/api/routes/files/uploadPOST.js

@@ -1,8 +1,6 @@
 const Route = require('../../structures/Route');
 const Route = require('../../structures/Route');
-const config = require('../../../../config');
 const path = require('path');
 const path = require('path');
 const Util = require('../../utils/Util');
 const Util = require('../../utils/Util');
-const db = require('knex')(config.server.database);
 const moment = require('moment');
 const moment = require('moment');
 const log = require('../../utils/Log');
 const log = require('../../utils/Log');
 const jetpack = require('fs-jetpack');
 const jetpack = require('fs-jetpack');
@@ -22,13 +20,13 @@ class uploadPOST extends Route {
 		super('/upload', 'post', { bypassAuth: true });
 		super('/upload', 'post', { bypassAuth: true });
 	}
 	}
 
 
-	async run(req, res) {
+	async run(req, res, db) {
 		const user = await Util.isAuthorized(req);
 		const user = await Util.isAuthorized(req);
-		if (!user && !config.uploads.allowAnonymousUploads) return res.status(401).json({ message: 'Not authorized to use this resource' });
+		if (!user && !process.env.PUBLIC_MODE) return res.status(401).json({ message: 'Not authorized to use this resource' });
-		return this.uploadFile(req, res, user);
+		return this.uploadFile(req, res, db, user);
 	}
 	}
 
 
-	async processFile(req, res, user, file) {
+	async processFile(req, res, db, user, file) {
 		/*
 		/*
 			Check if the user is trying to upload to an album
 			Check if the user is trying to upload to an album
 		*/
 		*/
@@ -55,38 +53,37 @@ class uploadPOST extends Route {
 					We got a chunk that is not the last part, send smoke signal that we received it.
 					We got a chunk that is not the last part, send smoke signal that we received it.
 				*/
 				*/
 				return res.json({ message: 'Successfully uploaded chunk' });
 				return res.json({ message: 'Successfully uploaded chunk' });
-			} else {
+			}
-				/*
+			/*
-					Seems we finally got the last part of a chunk upload
+				Seems we finally got the last part of a chunk upload
-				*/
+			*/
-				const uploadsDir = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder);
+			const uploadsDir = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER);
-				const chunkedFileDir = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder, 'chunks', file.body.uuid);
+			const chunkedFileDir = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER, 'chunks', file.body.uuid);
-				const chunkFiles = await jetpack.findAsync(chunkedFileDir, { matching: '*' });
+			const chunkFiles = await jetpack.findAsync(chunkedFileDir, { matching: '*' });
-				const originalname = Util.getFilenameFromPath(chunkFiles[0].substring(0, chunkFiles[0].lastIndexOf('.')));
+			const originalname = Util.getFilenameFromPath(chunkFiles[0].substring(0, chunkFiles[0].lastIndexOf('.')));
-
+
-				const tempFile = {
+			const tempFile = {
-					filename: Util.getUniqueFilename(originalname),
+				filename: Util.getUniqueFilename(originalname),
-					originalname,
+				originalname,
-					size: file.body.totalfilesize
+				size: file.body.totalfilesize
-				};
+			};
-
+
-				for (const chunkFile of chunkFiles) {
+			for (const chunkFile of chunkFiles) {
-					try {
-						const data = await jetpack.readAsync(chunkFile, 'buffer'); // eslint-disable-line no-await-in-loop
-						await jetpack.appendAsync(path.join(uploadsDir, tempFile.filename), data); // eslint-disable-line no-await-in-loop
-					} catch (error) {
-						log.error(error);
-					}
-				}
-
 				try {
 				try {
-					await jetpack.removeAsync(chunkedFileDir);
+					const data = await jetpack.readAsync(chunkFile, 'buffer'); // eslint-disable-line no-await-in-loop
+					await jetpack.appendAsync(path.join(uploadsDir, tempFile.filename), data); // eslint-disable-line no-await-in-loop
 				} catch (error) {
 				} catch (error) {
 					log.error(error);
 					log.error(error);
 				}
 				}
+			}
 
 
-				upload = tempFile;
+			try {
+				await jetpack.removeAsync(chunkedFileDir);
+			} catch (error) {
+				log.error(error);
 			}
 			}
+
+			upload = tempFile;
 		}
 		}
 
 
 		/*
 		/*
@@ -109,7 +106,7 @@ class uploadPOST extends Route {
 				message: 'Successfully uploaded file BUT IT EXISTED ALREADY',
 				message: 'Successfully uploaded file BUT IT EXISTED ALREADY',
 				name: exists.name,
 				name: exists.name,
 				size: exists.size,
 				size: exists.size,
-				url: `${config.filesServeLocation}/${exists.name}`
+				url: `${process.env.DOMAIN}/${exists.name}`
 			});
 			});
 
 
 			return Util.deleteFile(upload.filename);
 			return Util.deleteFile(upload.filename);
@@ -147,7 +144,7 @@ class uploadPOST extends Route {
 			message: 'Successfully uploaded file',
 			message: 'Successfully uploaded file',
 			name: upload.filename,
 			name: upload.filename,
 			size: upload.size,
 			size: upload.size,
-			url: `${config.filesServeLocation}/${upload.filename}`
+			url: `${process.env.DOMAIN}/${upload.filename}`
 		});
 		});
 
 
 		/*
 		/*
@@ -167,7 +164,7 @@ class uploadPOST extends Route {
 		/*
 		/*
 			If exif removal has been force service-wide or requested by the user, remove it
 			If exif removal has been force service-wide or requested by the user, remove it
 		*/
 		*/
-		if (config.uploads.forceStripExif) { // || user.settings.stripExif) {
+		if (process.env.STRIP_EXIF) { // || user.settings.stripExif) {
 			// Util.removeExif(upload.filename);
 			// Util.removeExif(upload.filename);
 		}
 		}
 
 
@@ -177,11 +174,11 @@ class uploadPOST extends Route {
 		return Util.generateThumbnails(upload.filename);
 		return Util.generateThumbnails(upload.filename);
 	}
 	}
 
 
-	uploadFile(req, res, user) {
+	uploadFile(req, res, db, user) {
 		const busboy = new Busboy({
 		const busboy = new Busboy({
 			headers: req.headers,
 			headers: req.headers,
 			limits: {
 			limits: {
-				fileSize: config.uploads.uploadMaxSize * (1000 * 1000),
+				fileSize: process.env.MAX_SIZE * (1000 * 1000),
 				files: 1
 				files: 1
 			}
 			}
 		});
 		});
@@ -209,7 +206,8 @@ class uploadPOST extends Route {
 			Hey ther's a file! Let's upload it.
 			Hey ther's a file! Let's upload it.
 		*/
 		*/
 		busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
 		busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
-			let name, saveTo;
+			let name;
+			let saveTo;
 
 
 			/*
 			/*
 				Let check whether the file is part of a chunk upload or if it's a standalone one.
 				Let check whether the file is part of a chunk upload or if it's a standalone one.
@@ -219,15 +217,15 @@ class uploadPOST extends Route {
 			const ext = path.extname(filename).toLowerCase();
 			const ext = path.extname(filename).toLowerCase();
 			if (Util.isExtensionBlocked(ext)) return res.status(400).json({ message: 'This extension is not allowed.' });
 			if (Util.isExtensionBlocked(ext)) return res.status(400).json({ message: 'This extension is not allowed.' });
 
 
-			if (!fileToUpload.body.uuid) {
+			if (fileToUpload.body.uuid) {
-				name = Util.getUniqueFilename(filename);
-				if (!name) return res.status(500).json({ message: 'There was a problem allocating a filename for your upload' });
-				saveTo = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder, name);
-			} else {
 				name = `${filename}.${fileToUpload.body.chunkindex}`;
 				name = `${filename}.${fileToUpload.body.chunkindex}`;
-				const chunkDir = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder, 'chunks', fileToUpload.body.uuid);
+				const chunkDir = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER, 'chunks', fileToUpload.body.uuid);
 				jetpack.dir(chunkDir);
 				jetpack.dir(chunkDir);
-				saveTo = path.join(__dirname, '..', '..', '..', '..', config.uploads.uploadFolder, 'chunks', fileToUpload.body.uuid, name);
+				saveTo = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER, 'chunks', fileToUpload.body.uuid, name);
+			} else {
+				name = Util.getUniqueFilename(filename);
+				if (!name) return res.status(500).json({ message: 'There was a problem allocating a filename for your upload' });
+				saveTo = path.join(__dirname, '..', '..', '..', '..', process.env.UPLOAD_FOLDER, name);
 			}
 			}
 
 
 			/*
 			/*
@@ -269,7 +267,7 @@ class uploadPOST extends Route {
 			return res.status(500).json({ message: 'There was an error uploading the file.' });
 			return res.status(500).json({ message: 'There was an error uploading the file.' });
 		});
 		});
 
 
-		busboy.on('finish', () => this.processFile(req, res, user, fileToUpload));
+		busboy.on('finish', () => this.processFile(req, res, db, user, fileToUpload));
 		req.pipe(busboy);
 		req.pipe(busboy);
 	}
 	}
 }
 }

+ 1 - 1
src/api/routes/verifyGET.js

@@ -5,7 +5,7 @@ class verifyGET extends Route {
 		super('/verify', 'get');
 		super('/verify', 'get');
 	}
 	}
 
 
-	run(req, res, user) {
+	run(req, res, db, user) {
 		const returnUser = {
 		const returnUser = {
 			id:	user.id,
 			id:	user.id,
 			username: user.username,
 			username: user.username,

+ 18 - 6
src/api/structures/Route.js

@@ -1,6 +1,13 @@
 const JWT = require('jsonwebtoken');
 const JWT = require('jsonwebtoken');
-const { server } = require('../../../config');
+const db = require('knex')({
-const db = require('knex')(server.database);
+	client: process.env.DB_CLIENT,
+	connection: {
+		host: process.env.DB_HOST,
+		user: process.env.DB_USER,
+		password: process.env.DB_PASS,
+		database: process.env.DB_DATABASE
+	}
+});
 const moment = require('moment');
 const moment = require('moment');
 const log = require('../utils/Log');
 const log = require('../utils/Log');
 
 
@@ -15,12 +22,12 @@ class Route {
 	}
 	}
 
 
 	authorize(req, res) {
 	authorize(req, res) {
-		if (this.options.bypassAuth) return this.run(req, res);
+		if (this.options.bypassAuth) return this.run(req, res, db);
 		if (!req.headers.authorization) return res.status(401).json({ message: 'No authorization header provided' });
 		if (!req.headers.authorization) return res.status(401).json({ message: 'No authorization header provided' });
 		const token = req.headers.authorization.split(' ')[1];
 		const token = req.headers.authorization.split(' ')[1];
 		if (!token) return res.status(401).json({ message: 'No authorization header provided' });
 		if (!token) return res.status(401).json({ message: 'No authorization header provided' });
 
 
-		return JWT.verify(token, server.secret, async (error, decoded) => {
+		return JWT.verify(token, process.env.SECRET, async (error, decoded) => {
 			if (error) {
 			if (error) {
 				log.error(error);
 				log.error(error);
 				return res.status(401).json({ message: 'Your token appears to be invalid' });
 				return res.status(401).json({ message: 'Your token appears to be invalid' });
@@ -33,13 +40,18 @@ class Route {
 			if (iat && iat < moment(user.passwordEditedAt).format('x')) return res.status(401).json({ message: 'Token expired' });
 			if (iat && iat < moment(user.passwordEditedAt).format('x')) return res.status(401).json({ message: 'Token expired' });
 			if (!user.enabled) return res.status(401).json({ message: 'This account has been disabled' });
 			if (!user.enabled) return res.status(401).json({ message: 'This account has been disabled' });
 
 
-			return this.run(req, res, user);
+			return this.run(req, res, db, user);
 		});
 		});
 	}
 	}
 
 
-	run(req, res, user) { // eslint-disable-line no-unused-vars
+	run(req, res, db) { // eslint-disable-line no-unused-vars
 		return;
 		return;
 	}
 	}
+
+	error(res, error) {
+		log.error(error);
+		return res.status(500).json({ message: 'There was a problem parsing the request' });
+	}
 }
 }
 
 
 module.exports = Route;
 module.exports = Route;

+ 7 - 25
src/api/structures/Server.js

@@ -1,4 +1,5 @@
-const config = require('../../../config');
+require('dotenv').config();
+
 const log = require('../utils/Log');
 const log = require('../utils/Log');
 const express = require('express');
 const express = require('express');
 const helmet = require('helmet');
 const helmet = require('helmet');
@@ -8,17 +9,16 @@ const bodyParser = require('body-parser');
 const jetpack = require('fs-jetpack');
 const jetpack = require('fs-jetpack');
 const path = require('path');
 const path = require('path');
 const Database = require('./Database');
 const Database = require('./Database');
-const oneliner = require('one-liner');
 
 
 const rateLimiter = new RateLimit({
 const rateLimiter = new RateLimit({
-	windowMs: config.server.rateLimits.window,
+	windowMs: process.env.RATE_LIMIT_WINDOW,
-	max: config.server.rateLimits.max,
+	max: process.env.RATE_LIMIT_MAX,
 	delayMs: 0
 	delayMs: 0
 });
 });
 
 
 class Server {
 class Server {
 	constructor() {
 	constructor() {
-		this.port = config.server.ports.backend;
+		this.port = process.env.SERVER_PORT;
 		this.server = express();
 		this.server = express();
 		this.server.set('trust proxy', 1);
 		this.server.set('trust proxy', 1);
 		this.server.use(helmet());
 		this.server.use(helmet());
@@ -36,12 +36,6 @@ class Server {
 		// this.server.use(rateLimiter);
 		// this.server.use(rateLimiter);
 		this.routesFolder = path.join(__dirname, '..', 'routes');
 		this.routesFolder = path.join(__dirname, '..', 'routes');
 		this.database = new Database();
 		this.database = new Database();
-		this.server.get('/config', (req, res) => res.json({
-			baseURL: config.backendLocation,
-			serviceName: config.serviceName,
-			maxFileSize: config.uploads.uploadMaxSize,
-			chunkSize: config.uploads.chunkSize
-		}));
 	}
 	}
 
 
 	registerAllTheRoutes() {
 	registerAllTheRoutes() {
@@ -51,24 +45,12 @@ class Server {
 			if (Array.isArray(RouteClass)) routes = RouteClass;
 			if (Array.isArray(RouteClass)) routes = RouteClass;
 			for (const File of routes) {
 			for (const File of routes) {
 				const route = new File();
 				const route = new File();
-				this.server[route.method](config.server.routePrefix + route.path, route.authorize.bind(route));
+				this.server[route.method](process.env.ROUTE_PREFIX + route.path, route.authorize.bind(route));
-				log.info(`Found route ${route.method.toUpperCase()} ${config.server.routePrefix}${route.path}`);
+				log.info(`Found route ${route.method.toUpperCase()} ${process.env.ROUTE_PREFIX}${route.path}`);
 			}
 			}
 		});
 		});
 	}
 	}
 
 
-	writeFrontendConfig() {
-		const template = oneliner`
-			module.exports = {
-				baseURL: '${config.backendLocation}',
-				serviceName: '${config.serviceName}',
-				maxFileSize: '${config.uploads.uploadMaxSize}',
-				chunkSize: '${config.uploads.chunkSize}'
-			}`;
-		jetpack.write(path.join(__dirname, '..', '..', 'frontend', 'config.js'), template);
-		log.success('Frontend config file generated successfully');
-	}
-
 	start() {
 	start() {
 		jetpack.dir('uploads/chunks');
 		jetpack.dir('uploads/chunks');
 		jetpack.dir('uploads/thumbs/square');
 		jetpack.dir('uploads/thumbs/square');

+ 38 - 28
src/api/utils/Util.js

@@ -1,9 +1,17 @@
-const config = require('../../../config');
+// const config = require('../../../config');
 const jetpack = require('fs-jetpack');
 const jetpack = require('fs-jetpack');
 const randomstring = require('randomstring');
 const randomstring = require('randomstring');
 const path = require('path');
 const path = require('path');
 const JWT = require('jsonwebtoken');
 const JWT = require('jsonwebtoken');
-const db = require('knex')(config.server.database);
+const db = require('knex')({
+	client: process.env.DB_CLIENT,
+	connection: {
+		host: process.env.DB_HOST,
+		user: process.env.DB_USER,
+		password: process.env.DB_PASS,
+		database: process.env.DB_DATABASE
+	}
+});
 const moment = require('moment');
 const moment = require('moment');
 const log = require('../utils/Log');
 const log = require('../utils/Log');
 const crypto = require('crypto');
 const crypto = require('crypto');
@@ -13,10 +21,11 @@ const Zip = require('adm-zip');
 
 
 const imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png', '.webp'];
 const imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png', '.webp'];
 const videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov'];
 const videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov'];
+const blockedExtensions = process.env.BLOCKED_EXTENSIONS.split(',');
 
 
 class Util {
 class Util {
 	static isExtensionBlocked(extension) {
 	static isExtensionBlocked(extension) {
-		return config.uploads.blockedExtensions.includes(extension);
+		return blockedExtensions.includes(extension);
 	}
 	}
 
 
 	static generateThumbnails(filename) {
 	static generateThumbnails(filename) {
@@ -36,38 +45,38 @@ class Util {
 		const ExifTransformer = require('exif-be-gone');
 		const ExifTransformer = require('exif-be-gone');
 		const toStream = require('buffer-to-stream');
 		const toStream = require('buffer-to-stream');
 
 
-		const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename), 'buffer');
+		const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename), 'buffer');
-		const writer = jetpack.createWriteStream(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, `${filename}.noexif`));
+		const writer = jetpack.createWriteStream(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, `${filename}.noexif`));
 		toStream(file).pipe(new ExifTransformer()).pipe(writer);
 		toStream(file).pipe(new ExifTransformer()).pipe(writer);
 	}
 	}
 	*/
 	*/
 
 
 	static async generateThumbnailForImage(filename, output) {
 	static async generateThumbnailForImage(filename, output) {
-		const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename), 'buffer');
+		const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename), 'buffer');
 		await sharp(file)
 		await sharp(file)
 			.resize(64, 64)
 			.resize(64, 64)
 			.toFormat('png')
 			.toFormat('png')
-			.toFile(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', 'square', output));
+			.toFile(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'thumbs', 'square', output));
 		await sharp(file)
 		await sharp(file)
 			.resize(225, null)
 			.resize(225, null)
 			.toFormat('png')
 			.toFormat('png')
-			.toFile(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', output));
+			.toFile(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'thumbs', output));
 	}
 	}
 
 
 	static generateThumbnailForVideo(filename) {
 	static generateThumbnailForVideo(filename) {
-		ffmpeg(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename))
+		ffmpeg(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename))
 			.thumbnail({
 			.thumbnail({
 				timestamps: [0],
 				timestamps: [0],
 				filename: '%b.png',
 				filename: '%b.png',
-				folder: path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', 'square'),
+				folder: path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'thumbs', 'square'),
 				size: '64x64'
 				size: '64x64'
 			})
 			})
 			.on('error', error => log.error(error.message));
 			.on('error', error => log.error(error.message));
-		ffmpeg(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename))
+		ffmpeg(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename))
 			.thumbnail({
 			.thumbnail({
 				timestamps: [0],
 				timestamps: [0],
 				filename: '%b.png',
 				filename: '%b.png',
-				folder: path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs'),
+				folder: path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'thumbs'),
 				size: '150x?'
 				size: '150x?'
 			})
 			})
 			.on('error', error => log.error(error.message));
 			.on('error', error => log.error(error.message));
@@ -80,11 +89,11 @@ class Util {
 	}
 	}
 
 
 	static constructFilePublicLink(file) {
 	static constructFilePublicLink(file) {
-		file.url = `${config.filesServeLocation}/${file.name}`;
+		file.url = `${process.env.DOMAIN}/${file.name}`;
 		const thumb = this.getFileThumbnail(file.name);
 		const thumb = this.getFileThumbnail(file.name);
 		if (thumb) {
 		if (thumb) {
-			file.thumb = `${config.filesServeLocation}/thumbs/${thumb}`;
+			file.thumb = `${process.env.DOMAIN}/thumbs/${thumb}`;
-			file.thumbSquare = `${config.filesServeLocation}/thumbs/square/${thumb}`;
+			file.thumbSquare = `${process.env.DOMAIN}/thumbs/square/${thumb}`;
 		}
 		}
 		return file;
 		return file;
 	}
 	}
@@ -92,12 +101,13 @@ class Util {
 	static getUniqueFilename(name) {
 	static getUniqueFilename(name) {
 		const retry = (i = 0) => {
 		const retry = (i = 0) => {
 			const filename = randomstring.generate({
 			const filename = randomstring.generate({
-				length: config.uploads.generatedFilenameLength,
+				length: process.env.GENERATED_FILENAME_LENGTH,
 				capitalization: 'lowercase'
 				capitalization: 'lowercase'
 			}) + path.extname(name);
 			}) + path.extname(name);
-			const exists = jetpack.exists(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename));
+			const exists = jetpack.exists(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename));
 			if (!exists) return filename;
 			if (!exists) return filename;
-			if (i < config.uploads.retryFilenameTimes) return retry(i + 1);
+			if (i < 5) return retry(i + 1);
+			log.error('Couldnt allocate identifier for file');
 			return null;
 			return null;
 		};
 		};
 		return retry();
 		return retry();
@@ -106,7 +116,7 @@ class Util {
 	static getUniqueAlbumIdentifier() {
 	static getUniqueAlbumIdentifier() {
 		const retry = async (i = 0) => {
 		const retry = async (i = 0) => {
 			const identifier = randomstring.generate({
 			const identifier = randomstring.generate({
-				length: config.albums.generatedAlbumLinkLength,
+				length: process.env.GENERATED_ALBUM_LENGTH,
 				capitalization: 'lowercase'
 				capitalization: 'lowercase'
 			});
 			});
 			const exists = await db.table('links').where({ identifier }).first();
 			const exists = await db.table('links').where({ identifier }).first();
@@ -114,7 +124,7 @@ class Util {
 			/*
 			/*
 				It's funny but if you do i++ the asignment never gets done resulting in an infinite loop
 				It's funny but if you do i++ the asignment never gets done resulting in an infinite loop
 			*/
 			*/
-			if (i < config.albums.retryAlbumLinkTimes) return retry(i + 1);
+			if (i < 5) return retry(i + 1);
 			log.error('Couldnt allocate identifier for album');
 			log.error('Couldnt allocate identifier for album');
 			return null;
 			return null;
 		};
 		};
@@ -122,7 +132,7 @@ class Util {
 	}
 	}
 
 
 	static async getFileHash(filename) {
 	static async getFileHash(filename) {
-		const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename), 'buffer');
+		const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename), 'buffer');
 		if (!file) {
 		if (!file) {
 			log.error(`There was an error reading the file < ${filename} > for hashing`);
 			log.error(`There was an error reading the file < ${filename} > for hashing`);
 			return null;
 			return null;
@@ -140,9 +150,9 @@ class Util {
 	static async deleteFile(filename, deleteFromDB = false) {
 	static async deleteFile(filename, deleteFromDB = false) {
 		const thumbName = this.getFileThumbnail(filename);
 		const thumbName = this.getFileThumbnail(filename);
 		try {
 		try {
-			await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename));
+			await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename));
-			await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', thumbName));
+			await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'thumbs', thumbName));
-			await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', 'square', thumbName));
+			await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'thumbs', 'square', thumbName));
 			if (deleteFromDB) {
 			if (deleteFromDB) {
 				await db.table('files').where('name', filename).delete();
 				await db.table('files').where('name', filename).delete();
 			}
 			}
@@ -156,7 +166,7 @@ class Util {
 		try {
 		try {
 			const files = await db.table('files').where({ albumId: id });
 			const files = await db.table('files').where({ albumId: id });
 			for (const file of files) {
 			for (const file of files) {
-				await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, file));
+				await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, file));
 			}
 			}
 			await db.table('files').where({ albumId: id }).delete();
 			await db.table('files').where({ albumId: id }).delete();
 		} catch (error) {
 		} catch (error) {
@@ -169,7 +179,7 @@ class Util {
 		const token = req.headers.authorization.split(' ')[1];
 		const token = req.headers.authorization.split(' ')[1];
 		if (!token) return false;
 		if (!token) return false;
 
 
-		return JWT.verify(token, config.server.secret, async (error, decoded) => {
+		return JWT.verify(token, process.env.SECRET, async (error, decoded) => {
 			if (error) {
 			if (error) {
 				log.error(error);
 				log.error(error);
 				return false;
 				return false;
@@ -189,9 +199,9 @@ class Util {
 		try {
 		try {
 			const zip = new Zip();
 			const zip = new Zip();
 			for (const file of files) {
 			for (const file of files) {
-				zip.addLocalFile(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, file));
+				zip.addLocalFile(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, file));
 			}
 			}
-			zip.writeZip(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'zips', `${album.userId}-${album.id}.zip`));
+			zip.writeZip(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, 'zips', `${album.userId}-${album.id}.zip`));
 		} catch (error) {
 		} catch (error) {
 			log.error(error);
 			log.error(error);
 		}
 		}

+ 67 - 18
src/wizard.js

@@ -1,3 +1,4 @@
+const jetpack = require('fs-jetpack');
 const qoa = require('qoa');
 const qoa = require('qoa');
 qoa.config({
 qoa.config({
 	prefix: '>',
 	prefix: '>',
@@ -18,69 +19,117 @@ async function start() {
 		{
 		{
 			type: 'input',
 			type: 'input',
 			query: 'Port to run the API in:',
 			query: 'Port to run the API in:',
-			handle: 'serverPort'
+			handle: 'SERVER_PORT'
 		},
 		},
 		{
 		{
 			type: 'input',
 			type: 'input',
 			query: 'Port to run the Website in:',
 			query: 'Port to run the Website in:',
-			handle: 'websitePort'
+			handle: 'WEBSITE_PORT'
 		},
 		},
 		{
 		{
 			type: 'input',
 			type: 'input',
 			query: 'Full domain this instance is gonna be running on (Ex: https://lolisafe.moe):',
 			query: 'Full domain this instance is gonna be running on (Ex: https://lolisafe.moe):',
-			handle: 'fullDomain'
+			handle: 'DOMAIN'
+		},
+		{
+			type: 'input',
+			query: 'Name of the service? (Ex: lolisafe):',
+			handle: 'SERVICE_NAME'
 		},
 		},
 		{
 		{
 			type: 'input',
 			type: 'input',
 			query: 'Maximum allowed upload file size in MB (Ex: 100):',
 			query: 'Maximum allowed upload file size in MB (Ex: 100):',
-			handle: 'maxSize'
+			handle: 'MAX_SIZE'
 		},
 		},
 		{
 		{
 			type: 'confirm',
 			type: 'confirm',
 			query: 'Generate thumbnails for images/videos? (Requires ffmpeg installed and in your PATH)',
 			query: 'Generate thumbnails for images/videos? (Requires ffmpeg installed and in your PATH)',
-			handle: 'generateThumbnails',
+			handle: 'GENERATE_THUMBNAILS',
 			accept: 'y',
 			accept: 'y',
 			deny: 'n'
 			deny: 'n'
 		},
 		},
 		{
 		{
 			type: 'confirm',
 			type: 'confirm',
 			query: 'Allow users to download entire albums in ZIP format?',
 			query: 'Allow users to download entire albums in ZIP format?',
-			handle: 'generateZips',
+			handle: 'GENERATE_ZIPS',
 			accept: 'y',
 			accept: 'y',
 			deny: 'n'
 			deny: 'n'
 		},
 		},
 		{
 		{
-			type: 'interactive',
+			type: 'confirm',
-			query: 'How would you like to serve the uploaded files?',
+			query: 'Strip EXIF information from uploaded images if possible?',
-			handle: 'serveWithNode',
+			handle: 'STRIP_EXIF',
-			menu: [
+			accept: 'y',
-				'With NGINX (Faster but needs a bit more setup)',
+			deny: 'n'
-				'With node'
-			]
 		},
 		},
 		{
 		{
 			type: 'confirm',
 			type: 'confirm',
-			query: 'Run lolisafe in public mode?',
+			query: 'Serve files with node?',
-			handle: 'publicMode',
+			handle: 'SERVE_WITH_NODE',
+			accept: 'y',
+			deny: 'n'
+		},
+		{
+			type: 'input',
+			query: 'Base number of characters for generated file URLs (12 should be good enough):',
+			handle: 'GENERATED_FILENAME_LENGTH'
+		},
+		{
+			type: 'input',
+			query: 'Base number of characters for generated album URLs (6 should be enough):',
+			handle: 'GENERATED_ALBUM_LENGTH'
+		},
+		{
+			type: 'confirm',
+			query: 'Run lolisafe in public mode? (People will be able to upload without an account)',
+			handle: 'PUBLIC_MODE',
 			accept: 'y',
 			accept: 'y',
 			deny: 'n'
 			deny: 'n'
 		},
 		},
 		{
 		{
 			type: 'confirm',
 			type: 'confirm',
 			query: 'Enable user signup for new accounts?',
 			query: 'Enable user signup for new accounts?',
-			handle: 'enableUserAccounts',
+			handle: 'USER_ACCOUNTS',
 			accept: 'y',
 			accept: 'y',
 			deny: 'n'
 			deny: 'n'
 		},
 		},
 		{
 		{
 			type: 'secure',
 			type: 'secure',
 			query: 'Type a secure password for the root user:',
 			query: 'Type a secure password for the root user:',
-			handle: 'rootPassword'
+			handle: 'ROOT_PASSWORD'
 		}
 		}
 	];
 	];
 
 
 	const response = await qoa.prompt(wizard);
 	const response = await qoa.prompt(wizard);
-	console.log(response);
+	let envfile = '';
+
+	const defaultSettings = {
+		CHUNK_SIZE: 90,
+		ROUTE_PREFIX: '/api',
+		RATE_LIMIT_WINDOW: 2,
+		RATE_LIMIT_MAX: 5,
+		DB_CLIENT: 'pg',
+		DB_HOST: 'localhost',
+		DB_USER: '',
+		DB_PASSWORD: '',
+		DB_DATABASE: '',
+		BLOCKED_EXTENSIONS: ['.jar', '.exe', '.msi', '.com', '.bat', '.cmd', '.scr', '.ps1', '.sh'],
+		UPLOAD_FOLDER: 'uploads',
+		SECRET: 'SuperSecretPassphraseHere',
+		MAX_LINKS_PER_ALBUM: 5
+	};
+
+	const allSettings = Object.assign(defaultSettings, response);
+
+	const keys = Object.keys(allSettings);
+	for (const item of keys) {
+		envfile += `${item}=${allSettings[item]}\n`;
+	}
+	jetpack.write('.env', envfile);
+
+	console.log();
+	console.log('== .env file generated successfully. You can now run lolisafe ==');
+	console.log();
 }
 }
 
 
 start();
 start();

+ 10 - 0
yarn.lock

@@ -3123,6 +3123,11 @@ dot-prop@^4.1.1:
   dependencies:
   dependencies:
     is-obj "^1.0.0"
     is-obj "^1.0.0"
 
 
+dotenv@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
+  integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
+
 dropzone@^5.5.0:
 dropzone@^5.5.0:
   version "5.5.1"
   version "5.5.1"
   resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-5.5.1.tgz#06e2f513e61d6aa363d4b556f18574f47cf7ba26"
   resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-5.5.1.tgz#06e2f513e61d6aa363d4b556f18574f47cf7ba26"
@@ -7471,6 +7476,11 @@ q@^1.1.2:
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
 
 
+qoa@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/qoa/-/qoa-0.2.0.tgz#2e4ea56a388982da570bf2025733b72e2f857aab"
+  integrity sha512-svEO3uevNU354fUBWgMxGJH0spF29EZRe140YL20PP+5C25V+u0eMeFforSIiop2879uXgxI+IFwibHBGcseEA==
+
 qs@6.5.2, qs@~6.5.2:
 qs@6.5.2, qs@~6.5.2:
   version "6.5.2"
   version "6.5.2"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"