Skip to content

Commit

Permalink
πŸš€ v0.0.1-alpha.2 (#14)
Browse files Browse the repository at this point in the history
* βž• App >> Adding '@nuxtjs/auth'.

* ✨ Login >> Creating Authentication Form and Login page. No function behind it yet.

* ✨ Register >> Adding register page.

* πŸ›‚ Auth >> Securing frontend pages only accessible after login, except for a few exceptions.

* πŸ’„ Sidebar >> Hiding pages if user is not logged in.

* πŸ‘½οΈ REST Api >> Adding empty endpoints for user authentication.

* βž• App >> Adding 'mongoose' db.

* βž• App >> Adding 'bcrypt' for encprytion.

* 🎨 Controller >> Changing naming for scripts controller.

* βž– App >> Dont wanna mongoose!

* βž• App >> Adding 'lowdb'. Should be enough for my purpose! πŸ˜‚

* 🎨 Scripts >> Changing controller naming.

* πŸ‘½οΈ REST Api >> Adding modul, controller and route to register a user. No checks right now... just dumb registering in a local lowdb database.

* βž• App >> Adding passport and JWT packages.

* πŸ™ˆ GitIgnore >> Adding database file.

* βž• App >> Adding 'cookie-parser' for express server.

* πŸ‘½οΈ REST Api >> Adding login and user authentication handling.

* βž• App >> Nuxt Auth Modul gewechselt.

* πŸ‘½οΈ REST Api >> Less response messages on auth routes.

* πŸ—οΈ Nuxt/Express >> Changing/separating folder structure.

* βž• App >> Adding "concurrently" for starting multiple scripts at once.

* πŸ‘½οΈ Nuxt >> Deactivating Auth for now.

* πŸ‘½οΈ REST Api >> Separating routes from app. Also deactivating auth for now.

* ⬆️ App >> Updating [email protected]

* ♻️ REST Api >> Refactoring Express server. Using ESM Syntax.

* βž• App >> Adding dotenv for using .env variables.

* ♻️ REST Api >> Refactoring Auth moduls for ESM Syntax.

* 🎨 App >> Activating Auth Modul in Nuxt. Not functioning right now!

* πŸ› REST Api >> Bugfixing jwt auth implementation.

* 🚨 Linter >> Fixing Linter settings.

* πŸ’„ Login >> Showing user info if already logged in.

* ✨ Logout >> Sidebar button - logging out.

* πŸ’„ App >> Better Update Display.

* πŸ‘½οΈ REST Api >> Only allows one user registration right now. Frontend is reacting to this and only displays login/register pages if possible.

* 🎨 Auth >> Preparations for creating a secret token if no one exists.

* πŸ”’οΈ Authentication >> Refactoring the generation of secret key.

* πŸ”₯ App >> Removing default nuxt files.

* πŸ’₯ App >> Changing nuxt to client side rendering. Hole app is gonna served over the express server.

* πŸ’₯ App >> Changing PM2 starting script.

* πŸ”‡ App >> Removing dev log.

* βž• App >> Adding cross-env for setting cross platform env variable.

* πŸ”¨ App >> Testing Env setting

* πŸ”Š App >> Changing console.logs for App start.

* πŸš€ v0.0.1-alpha.2
  • Loading branch information
borsTiHD authored Jun 5, 2021
1 parent 2ee5f6c commit 613166f
Show file tree
Hide file tree
Showing 36 changed files with 1,411 additions and 205 deletions.
File renamed without changes.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,6 @@ sw.*

# Custom user scripts
/scripts/custom

# Database
db.json
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ $ yarn install
$ yarn dev

# build for production and launch server (localhost:8800)
$ yarn build
$ yarn generate
$ yarn start
```

Expand Down
57 changes: 57 additions & 0 deletions api/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Imports
import createError from 'http-errors'
import express from 'express'
import cors from 'cors'
import cookieParser from 'cookie-parser'

// Authentication Import
import passport from 'passport'

// Importing Routes
import baseRoutes from './router.js'

// Development
const isDev = process.env.NODE_ENV === 'development'
console.log('[App] -> Development:', isDev)

// Config
const PORT = process.env.PORT || 8800 // Default Port: 8800

// Express Init
const app = express()
if (isDev) app.use(cors()) // CORS policy only in dev
app.use(express.static('dist'))
app.use(express.json())
app.use(express.urlencoded({ extended: true })) // for form data
app.use(cookieParser()) // Cookies
app.use(passport.initialize()) // Authentication

// Router/Endpoints
const baseUrl = '/api/v1'
baseRoutes(app, baseUrl)

// Catch 404 and forward to error handler
app.use((req, res, next) => {
next(createError(404))
})

// Error handler
app.use((err, req, res, next) => {
console.error('Request Url:', req.originalUrl)

// Set locals, only providing error in development
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'development' ? err : {}

// Render the error page
res.status(err.status || 500)
res.json({
_status: 'error',
err
})
})

// Listening on port
app.listen(PORT, () => {
console.log(`[App] -> App is running on ${PORT}`)
})
168 changes: 168 additions & 0 deletions api/controllers/authentication.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { join } from 'path'
import crypto from 'crypto'
import fs from 'fs-extra'
import bcrypt from 'bcrypt'
import passport from 'passport'
import LocalStrategy from 'passport-local'
import JwtStrategy from 'passport-jwt'
import jwt from 'jsonwebtoken'
import User from '../models/user.js'

// const authUserSecret = getAuthUserSecret() // process.env.AUTH_USER_SECRET
getAuthUserSecret()

// Returning or if it not exists generating a authentication secret key
async function getAuthUserSecret() {
// Getting Env if available
const secret = process.env.AUTH_USER_SECRET || false

// If Env variable is not existing, we will generate one
if (!secret) {
const generatedKey = crypto.randomBytes(256).toString('hex')
process.env.AUTH_USER_SECRET = generatedKey

// Saving key in '.env' file
const filePath = join('.', '.env')
const content = `AUTH_USER_SECRET=${generatedKey}`
await fs.outputFile(filePath, content).catch((error) => {
console.error(error)
throw error
})
return generatedKey
}
return secret
}

// Setting up passport for email/password authentication
passport.use(new LocalStrategy.Strategy({ usernameField: 'email', passwordField: 'password' }, async function(email, password, done) {
await User.GetUser(email)
.then((user) => {
return user
}).then(async(user) => {
if (!user) {
return done(null, false, { message: 'Authentication failed' })
}
const validation = await comparePasswords(password, user.password)
if (validation) {
return done(null, user)
} else {
return done(null, false, { message: 'Authentication failed' })
}
}).catch((err) => {
return done(err)
})
}))

// Extracting JWT Token out of cookies
const tokenExtractor = function(req) {
let token = null
let reqCookie = null
let reqHeader = null

// Extracting 'jwt' token from two different places
try {
if (req && req.cookies && req.cookies['auth._token.local']) {
reqCookie = req.cookies['auth._token.local']
} else if (req.req && req.req.cookies && req.req.cookies['auth._token.local']) {
reqCookie = req.req.cookies['auth._token.local']
} else if (req.headers && req.headers && req.headers.authorization) {
reqHeader = req.headers.authorization
} else if (req.req.headers && req.req.headers && req.req.headers.authorization) {
reqHeader = req.req.headers.authorization
}
} catch (error) {
// console.error(error)
return null
}

if (reqCookie) {
const rawToken = reqCookie.toString()
token = rawToken.slice(rawToken.indexOf(' ') + 1, rawToken.length)
} else if (reqHeader) {
token = reqHeader.slice(reqHeader.indexOf(' ') + 1, reqHeader.length)
}
return token
}

/*
const options = {
// tokenExtractor
// JwtStrategy.ExtractJwt.fromAuthHeaderAsBearerToken()
// JwtStrategy.ExtractJwt.fromExtractors([tokenExtractor, JwtStrategy.ExtractJwt.fromHeader('authorization'), JwtStrategy.ExtractJwt.fromAuthHeaderAsBearerToken()])
jwtFromRequest: tokenExtractor,
secretOrKey: process.env.AUTH_USER_SECRET // authUserSecret
}
*/

// Setting up passport for JWT Token authentication
passport.use(new JwtStrategy.Strategy({ jwtFromRequest: tokenExtractor, secretOrKey: process.env.AUTH_USER_SECRET }, async function(jwtPayload, done) {
await User.GetUser(jwtPayload.email) // Or 'GetUser' - without 'User.'
.then((user) => {
if (user) {
return done(null, {
email: user.email
})
} else {
return done(null, false, 'Failed')
}
})
.catch((err) => {
console.error(err)
return done(err)
})
}))

/**
* Take a string and return a generated hash
* @name generatePasswordHash
* @function
* @memberof module:routers/auth
*/
async function generatePasswordHash(plainPassword) {
return await bcrypt.hash(plainPassword, 12)
}

// Compares password from plaintext and hashed one
async function comparePasswords(plainPassword, hashedPassword) {
return await bcrypt.compare(plainPassword, hashedPassword)
}

/**
* Returns generated token for authentication
* @name signUserToken
* @function
* @memberof module:routers/auth
*/
function signUserToken(user) {
return jwt.sign({
id: user.id,
email: user.email
}, process.env.AUTH_USER_SECRET) // authUserSecret
}

/**
* Proxy for CreateUser model function - creates a user in database
* @name CreateUser
* @function
* @memberof module:routers/auth
*/
async function CreateUser(user) {
return await User.CreateUser(user)
}

/**
* Proxy for CountUsers model function - counts how many users registered, or returns false
* @name CountUsers
* @function
* @memberof module:routers/auth
*/
async function CountUsers() {
return await User.CountUsers()
}

export default {
CreateUser,
CountUsers,
generatePasswordHash,
signUserToken
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Imports
const path = require('path')
const fs = require('fs-extra')
import path from 'path'
import fs from 'fs-extra'

// Middleware
const asyncHandler = require('../middleware/asyncHandler') // Middleware for handling errors on promise calls
const isCustomScript = require('../middleware/isCustomScript') // Testing if given 'path' is a 'custom path'
const zipDirectory = require('../middleware/zipDirectory') // Zipping file/folder middleware
import asyncHandler from '../middleware/asyncHandler.js' // Middleware for handling errors on promise calls
import isCustomScript from '../middleware/isCustomScript.js' // Testing if given 'path' is a 'custom path'
import zipDirectory from '../middleware/zipDirectory.js' // Zipping file/folder middleware

// ChildProcess Spawn Import
const ChildProcessClass = require('../../classes/ChildProcessClass')
import ChildProcessClass from '../../classes/ChildProcessClass.js'
const childProcessSpawn = new ChildProcessClass()

// Script Directory
Expand All @@ -20,7 +20,7 @@ const scriptPath = path.join('.', 'scripts')
* @function
* @memberof module:routers/scripts
*/
exports.index = (req, res) => {
const index = (req, res) => {
res.json({
_status: 'ok',
info: 'Endpoint is set up.'
Expand All @@ -33,7 +33,7 @@ exports.index = (req, res) => {
* @function
* @memberof module:routers/scripts
*/
exports.list = asyncHandler(async(req, res, next) => {
const list = asyncHandler(async(req, res, next) => {
// Generates unique random IDs for every folder/file
const uniqueIds = []
let maxIds = 100
Expand Down Expand Up @@ -93,7 +93,7 @@ exports.list = asyncHandler(async(req, res, next) => {
* @param {string} script - Query for script path. Exp.: ?script=scripts%5Ccustom%5Ctest.bat
* @param {array} args - Query for arguments. Exp.: ?args=['a', 'b', 'c']
*/
exports.execute = asyncHandler(async(req, res, next) => {
const execute = asyncHandler(async(req, res, next) => {
const { query } = req
const scriptRaw = query.script
const args = query.args || []
Expand Down Expand Up @@ -142,7 +142,7 @@ exports.execute = asyncHandler(async(req, res, next) => {
* @param {string} name - Query for file name (just required for response). Exp.: ?name=test.bat
* @param {string} type - Query for file type (just required for response). Exp.: ?type=file
*/
exports.read = asyncHandler(async(req, res, next) => {
const read = asyncHandler(async(req, res, next) => {
// Query Data
const query = req.query
const { id, name, type, path } = query
Expand Down Expand Up @@ -176,7 +176,7 @@ exports.read = asyncHandler(async(req, res, next) => {
* @param {string} path - Query for file path. Exp.: ?path=scripts%5Ccustom%5Ctest.bat
* @param {string} name - Query for file name. Exp.: ?name=test.bat
*/
exports.download = asyncHandler(async(req, res, next) => {
const download = asyncHandler(async(req, res, next) => {
// Query Data
const query = req.query
const name = query.name
Expand Down Expand Up @@ -215,7 +215,7 @@ exports.download = asyncHandler(async(req, res, next) => {
* @memberof module:routers/scripts
* @param {object} data - Object -> form data. Delivers script data: '{ path: "scripts/custom/...", script: { name: "test", ext: "bat", text: "echo test" }}'
*/
exports.addFile = asyncHandler(async(req, res, next) => {
const addFile = asyncHandler(async(req, res, next) => {
const data = req.body
const script = data.script
const file = `${script.name}.${script.ext}`
Expand Down Expand Up @@ -259,7 +259,7 @@ exports.addFile = asyncHandler(async(req, res, next) => {
* @memberof module:routers/scripts
* @param {object} data - Object -> form data. Delivers folder data: '{ path: "scripts\custom", name: "test" }'
*/
exports.addFolder = asyncHandler(async(req, res, next) => {
const addFolder = asyncHandler(async(req, res, next) => {
const data = req.body
const folderName = data.name
const folderPath = data.path
Expand Down Expand Up @@ -300,7 +300,7 @@ exports.addFolder = asyncHandler(async(req, res, next) => {
* @memberof module:routers/scripts
* @param {string} path - Query for file/folder path. Exp.: ?path=scripts\custom\test
*/
exports.delete = asyncHandler(async(req, res, next) => {
const deleteFileOrFolder = asyncHandler(async(req, res, next) => {
// Query Data
const query = req.query
const filePath = query.path
Expand Down Expand Up @@ -363,7 +363,7 @@ exports.delete = asyncHandler(async(req, res, next) => {
* @param {string} oldFile - Query for old file data. Exp.: ?oldFile: {"path":"scripts\\custom\\test.bat","name":"test.bat","id":71,"type":"file"}
* @param {string} newFile - Query for new file data. Exp.: ?newFile: {"name":"new.bat","content":"echo test"}
*/
exports.editFile = asyncHandler(async(req, res, next) => {
const editFile = asyncHandler(async(req, res, next) => {
// Query Data
const query = req.query
const oldFile = JSON.parse(query.oldFile)
Expand Down Expand Up @@ -430,7 +430,7 @@ exports.editFile = asyncHandler(async(req, res, next) => {
* @param {string} oldFolder - Query for old file data. Exp.: ?oldFolder: {"path":"scripts\\custom\\test","name":"test"}
* @param {string} newFolder - Query for new file data. Exp.: ?newFolder: {"name":"new"}
*/
exports.editFolder = asyncHandler(async(req, res, next) => {
const editFolder = asyncHandler(async(req, res, next) => {
// Query Data
const query = req.query
const oldFolder = JSON.parse(query.oldFolder)
Expand Down Expand Up @@ -472,3 +472,16 @@ exports.editFolder = asyncHandler(async(req, res, next) => {
})
}
})

export default {
index,
list,
execute,
read,
download,
addFile,
addFolder,
deleteFileOrFolder,
editFile,
editFolder
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ const asyncUtil = (fn) =>
return Promise.resolve(fnReturn).catch(next)
}

module.exports = asyncUtil
export default asyncUtil
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ const isCustomScript = (path) => {
return /^scripts\\custom\\/gm.test(path) /* win path */ || /^scripts\/custom\//gm.test(path) /* linux path */
}

module.exports = isCustomScript
export default isCustomScript
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const fs = require('fs-extra')
const archiver = require('archiver')
import fs from 'fs-extra'
import archiver from 'archiver'

/**
* Middleware: Zipping a given file/folder
Expand All @@ -24,4 +24,4 @@ const zipDirectory = (source, out) => {
})
}

module.exports = zipDirectory
export default zipDirectory
Loading

0 comments on commit 613166f

Please sign in to comment.