Big Bang
This commit is contained in:
46
app/MiscUnit.js
Normal file
46
app/MiscUnit.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const Unit = require('libflitter/Unit')
|
||||
|
||||
/*
|
||||
* The Miscellaneous Unit
|
||||
* -------------------------------------------------------------
|
||||
* This is a Unit file where you should make any modifications that
|
||||
* your application may need such as custom Express add-ons. Changes
|
||||
* here are loaded after the core Flitter units, but before the other
|
||||
* units (secondary, custom, error, and App), so they are available to
|
||||
* other units in the stack.
|
||||
*/
|
||||
class MiscUnit extends Unit {
|
||||
|
||||
/*
|
||||
* Initializes the unit class.
|
||||
* This is called OUTSIDE of a Flitter context,
|
||||
* so no other contexts are available. Modifications here
|
||||
* take place before any part of Flitter is loaded or available.
|
||||
*/
|
||||
constructor(){
|
||||
super()
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the actual Unit.
|
||||
* This is where most of the changes will go.
|
||||
* This is provided an instance of the Express app
|
||||
* so any custom changes can be made. The core Flitter
|
||||
* contexts are available here, so things like config(),
|
||||
* model(), etc. work.
|
||||
*/
|
||||
async go(app, context){
|
||||
|
||||
// do stuff here
|
||||
|
||||
}
|
||||
|
||||
name(){
|
||||
return "misc"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = exports = MiscUnit
|
||||
0
app/assets/.gitkeep
Normal file
0
app/assets/.gitkeep
Normal file
10
app/assets/dash_v1.css
Normal file
10
app/assets/dash_v1.css
Normal file
@@ -0,0 +1,10 @@
|
||||
table, th, td {
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
BIN
app/assets/favicon.ico
Normal file
BIN
app/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/assets/flitter.png
Normal file
BIN
app/assets/flitter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
352
app/controllers/Auth.controller.js
Normal file
352
app/controllers/Auth.controller.js
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/controllers/Auth
|
||||
*/
|
||||
|
||||
const validator = require('validator')
|
||||
const bcrypt = require('bcrypt')
|
||||
const uuid = require('uuid/v4')
|
||||
|
||||
/**
|
||||
* Controller for the auth and user functionality for Flitter-auth.
|
||||
* @class
|
||||
*/
|
||||
class Auth {
|
||||
|
||||
/**
|
||||
* Create an authenticated session.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {module:flitter-auth/deploy/models/User~User} user - the user for which a session should be created
|
||||
*/
|
||||
create_auth_session(req, user){
|
||||
req.session.auth = {
|
||||
authenticated: true,
|
||||
uuid: user.uuid,
|
||||
user
|
||||
}
|
||||
|
||||
req.session.auth.user.password = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the authenticated session.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
*/
|
||||
destroy_auth_session(req){
|
||||
req.session.auth = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the registration page.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
*/
|
||||
register_get(req, res){
|
||||
|
||||
/*
|
||||
* Check for auth_form errors in the session.
|
||||
* If they exist, pass them along to the view.
|
||||
*/
|
||||
let submission_data = { error: false, data: {} }
|
||||
if ( req.session && 'auth_form' in req.session && 'errors' in req.session.auth_form ){
|
||||
submission_data = req.session.auth_form
|
||||
delete req.session.auth_form
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the registration view with the errors.
|
||||
*/
|
||||
_flitter.view(res, 'auth/register', { submission_data })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the registration and create a new user.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
*/
|
||||
register_post(req, res){
|
||||
|
||||
let username, password
|
||||
|
||||
/*
|
||||
* Creates a session variable with the auth_form errors.
|
||||
*/
|
||||
const create_error_session = (field, message) => {
|
||||
let auth_form = {
|
||||
error: true,
|
||||
errors: {
|
||||
|
||||
},
|
||||
data: {
|
||||
'username': req.body.username
|
||||
}
|
||||
}
|
||||
|
||||
auth_form.errors[field] = message
|
||||
req.session.auth_form = auth_form
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if username isn't provided.
|
||||
*/
|
||||
if ( !( 'username' in req.body ) ){
|
||||
req.session.auth_form = {
|
||||
error: true,
|
||||
errors: { 'username': 'Username is required.' }
|
||||
}
|
||||
|
||||
return res.redirect('/auth/register')
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if the password or password verification aren't provided.
|
||||
*/
|
||||
if ( !( 'password' in req.body ) || !( 'password_verify' in req.body ) ){
|
||||
req.session.auth_form = {
|
||||
error: true,
|
||||
errors: { 'password': 'Password and verification are required.' }
|
||||
}
|
||||
|
||||
return res.redirect('/auth/register')
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate that the username is alphanumeric.
|
||||
*/
|
||||
if ( !validator.isAlphanumeric( req.body.username ) ) {
|
||||
|
||||
/*
|
||||
* Create an auth_form error in the session and
|
||||
* redirect back to the registration form.
|
||||
*/
|
||||
create_error_session('username', 'Username must be alpha-numeric.')
|
||||
return res.redirect('/auth/register')
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the User model.
|
||||
*/
|
||||
const User = _flitter.model('User')
|
||||
|
||||
/*
|
||||
* Check if the username is already in the
|
||||
* database. If so, redirect with an error.
|
||||
*/
|
||||
User.find({ username: req.body.username }, (err, users) => {
|
||||
if ( users.length ){
|
||||
|
||||
/*
|
||||
* Create an auth_form error in the session and
|
||||
* redirect back to the registration form.
|
||||
*/
|
||||
create_error_session('username', 'Username is already taken.')
|
||||
return res.redirect('/auth/register')
|
||||
}
|
||||
username = req.body.username
|
||||
|
||||
/*
|
||||
* Check if the password meets the minimum length requirement.
|
||||
*/
|
||||
if ( !validator.isLength(req.body.password, {min:8}) ){
|
||||
|
||||
/*
|
||||
* Create an auth_form error in the session and
|
||||
* redirect back to the registration form.
|
||||
*/
|
||||
create_error_session('password', 'Password must be at least 8 characters.')
|
||||
return res.redirect('/auth/register')
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the password matches the password_verify field.
|
||||
*/
|
||||
if ( !validator.equals(req.body.password, req.body.password_verify) ){
|
||||
|
||||
/*
|
||||
* Create an auth_form error in the session and
|
||||
* redirect back to the registration form.
|
||||
*/
|
||||
create_error_session('password', 'Passwords don\'t match.')
|
||||
return res.redirect('/auth/register')
|
||||
}
|
||||
password = req.body.password
|
||||
|
||||
/*
|
||||
* Create the password hash.
|
||||
*/
|
||||
bcrypt.hash(password, 10, (err, hash) => {
|
||||
/*
|
||||
* Instantiate a new user object.
|
||||
*/
|
||||
const unique_id = uuid()
|
||||
const user = new User({
|
||||
username,
|
||||
password: hash,
|
||||
data: JSON.stringify({}),
|
||||
uuid: unique_id
|
||||
})
|
||||
|
||||
/*
|
||||
* Store the new user in the database.
|
||||
*/
|
||||
user.save().then(()=>{
|
||||
_flitter.controller('Auth').create_auth_session(req, user)
|
||||
return _flitter.view(res, 'auth/register_success')
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out and destroy the authenticated session.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
*/
|
||||
logout( req, res ){
|
||||
if ( req.session ){
|
||||
_flitter.controller('Auth').destroy_auth_session(req)
|
||||
}
|
||||
|
||||
return _flitter.view(res, 'auth/logged_out')
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the login view.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
*/
|
||||
login_get( req, res ){
|
||||
|
||||
/*
|
||||
* Check for auth_form errors in the session.
|
||||
* If they exist, pass them along to the view.
|
||||
*/
|
||||
let submission_data = { error: false, data: {} }
|
||||
if ( req.session && 'auth_form' in req.session && 'errors' in req.session.auth_form ){
|
||||
submission_data = req.session.auth_form
|
||||
delete req.session.auth_form
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the login view with the errors.
|
||||
*/
|
||||
return _flitter.view(res, 'auth/login', { submission_data })
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a login request.
|
||||
* If it is successful, create a user session and carry on.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
*/
|
||||
login_post( req, res ){
|
||||
const create_error_session = (field, message) => {
|
||||
let auth_form = {
|
||||
error: true,
|
||||
errors: {
|
||||
|
||||
},
|
||||
data: {
|
||||
'username': req.body.username
|
||||
}
|
||||
}
|
||||
|
||||
auth_form.errors[field] = message
|
||||
req.session.auth_form = auth_form
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if username isn't provided.
|
||||
*/
|
||||
if ( !( 'username' in req.body ) ){
|
||||
req.session.auth_form = {
|
||||
error: true,
|
||||
errors: { 'username': 'Username is required.' }
|
||||
}
|
||||
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail if the password isn't provided.
|
||||
*/
|
||||
if ( !( 'password' in req.body ) ){
|
||||
req.session.auth_form = {
|
||||
error: true,
|
||||
errors: { 'password': 'Password is required.' }
|
||||
}
|
||||
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the username is still alphanum.
|
||||
* Just a sanity check to prevent code injection.
|
||||
*/
|
||||
if ( !validator.isAlphanumeric( req.body.username ) ){
|
||||
create_error_session('username', "Invalid username.")
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
|
||||
const User = _flitter.model('User')
|
||||
User.findOne({username: req.body.username}, (err, user) => {
|
||||
|
||||
/*
|
||||
* If no user with the username is found, return
|
||||
* an error back.
|
||||
*/
|
||||
if ( !user ){
|
||||
create_error_session('username', 'Invalid username.')
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the provided password against the stored hash.
|
||||
*/
|
||||
bcrypt.compare( req.body.password, user.password, (err, match) => {
|
||||
|
||||
/*
|
||||
* Return an error if the password fails the hash.
|
||||
*/
|
||||
if ( !match ){
|
||||
create_error_session('password', 'Invalid password.')
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
else {
|
||||
|
||||
/*
|
||||
* Create an authenticated session.
|
||||
*/
|
||||
_flitter.controller('Auth').create_auth_session(req, user)
|
||||
|
||||
/*
|
||||
* If a destination was provided, redirect there.
|
||||
*/
|
||||
if ( req.session.destination ){
|
||||
const redirect_to = req.session.destination
|
||||
delete req.session.destination
|
||||
return res.redirect(redirect_to)
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, redirect to the provided sample dash.
|
||||
*/
|
||||
else {
|
||||
return res.redirect('/auth/dash')
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dash view.
|
||||
* By default, this is pretty simple. It's designed to be replaced by your app.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
*/
|
||||
dash_get(req, res){
|
||||
return _flitter.view(res, 'auth/dash', { user: req.session.auth.user })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Auth
|
||||
24
app/controllers/Home.controller.js
Normal file
24
app/controllers/Home.controller.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Home Controller
|
||||
* -------------------------------------------------------------
|
||||
* Controller for the main homepage of this Flitter app. Methods here
|
||||
* are used as handlers for routes specified in the route files.
|
||||
*/
|
||||
class Home {
|
||||
|
||||
/*
|
||||
* Serve the main welcome page.
|
||||
*/
|
||||
welcome(req, res){
|
||||
|
||||
/*
|
||||
* Return the welcome view.
|
||||
* It must be passed the response.
|
||||
* View parameters can be passed as an optional third
|
||||
* argument to the view() method.
|
||||
*/
|
||||
return _flitter.view(res, 'welcome')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Home
|
||||
58
app/controllers/api/v1.controller.js
Normal file
58
app/controllers/api/v1.controller.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* v1 Controller
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const Out = _flitter.model('v1:Out')
|
||||
const Project = _flitter.model('v1:Project')
|
||||
class v1 {
|
||||
|
||||
/*
|
||||
* Serve the main page.
|
||||
*/
|
||||
main(req, res){
|
||||
|
||||
/*
|
||||
* Return the main view.
|
||||
* It must be passed the response.
|
||||
* View parameters can be passed as an optional third
|
||||
* argument to the view() method.
|
||||
*/
|
||||
return view(res, 'view_name')
|
||||
}
|
||||
|
||||
async new_out( req, res, next ){
|
||||
console.log(req.body)
|
||||
const project = await Project.findOne({ uuid: req.params.key })
|
||||
|
||||
if ( !project ){
|
||||
return res.status(404).send({
|
||||
success: false,
|
||||
error: 'Project not found with specified key.'
|
||||
})
|
||||
}
|
||||
|
||||
if ( !req.body.data ){
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
error: 'Missing data.'
|
||||
})
|
||||
}
|
||||
|
||||
const data = JSON.parse(req.body.data)
|
||||
|
||||
const out = new Out({
|
||||
brief: data.brief,
|
||||
data: JSON.stringify(data.data),
|
||||
project_id: project.id
|
||||
})
|
||||
|
||||
await out.save()
|
||||
|
||||
return res.send({
|
||||
success: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = v1
|
||||
73
app/controllers/dash/v1.controller.js
Normal file
73
app/controllers/dash/v1.controller.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* v1 Controller
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const Project = _flitter.model('v1:Project')
|
||||
const Out = _flitter.model('v1:Out')
|
||||
class v1 {
|
||||
|
||||
/*
|
||||
* Serve the main page.
|
||||
*/
|
||||
async main(req, res){
|
||||
|
||||
const projects = await Project.find({ archived: false, user_id: req.session.auth.uuid })
|
||||
|
||||
/*
|
||||
* Return the main view.
|
||||
* It must be passed the response.
|
||||
* View parameters can be passed as an optional third
|
||||
* argument to the view() method.
|
||||
*/
|
||||
return _flitter.view(res, 'dash_v1:main', { projects })
|
||||
}
|
||||
|
||||
new_project_show(req, res, next){
|
||||
return _flitter.view(res, 'dash_v1:project', {})
|
||||
}
|
||||
|
||||
async new_project_do(req, res, next){
|
||||
if ( !req.body.name ){
|
||||
return view(res, 'dash_v1:project', {errors: ['Project name is required.']})
|
||||
}
|
||||
|
||||
const project = new Project({
|
||||
name: req.body.name,
|
||||
user_id: req.session.auth.uuid,
|
||||
data: JSON.stringify({
|
||||
created: Date.now(),
|
||||
modified: Date.now()
|
||||
}),
|
||||
shared_user_ids: [],
|
||||
})
|
||||
|
||||
await project.save()
|
||||
|
||||
return res.redirect('/dash/v1')
|
||||
}
|
||||
|
||||
async project_view(req, res, next){
|
||||
const project = await Project.findById(req.params.id)
|
||||
const outs = await Out.find({ project_id: project.id }).sort('-created')
|
||||
|
||||
if ( !project ){
|
||||
_flitter.error(res, 404, 'Project not found.')
|
||||
}
|
||||
|
||||
return _flitter.view(res, 'dash_v1:view', { project, outs })
|
||||
}
|
||||
|
||||
async out_view(req, res, next){
|
||||
const out = await Out.findById(req.params.id)
|
||||
|
||||
console.log(out.data)
|
||||
|
||||
const pretty = JSON.stringify(JSON.parse(out.data), null, 4)
|
||||
|
||||
// TODO permission access check
|
||||
return _flitter.view(res, 'dash_v1:out', {out, prettyd:pretty});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = v1
|
||||
12
app/models/Greeting.model.js
Normal file
12
app/models/Greeting.model.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Greeting Model
|
||||
* -------------------------------------------------------------
|
||||
* This is a sample model. The schema or structure of the model should
|
||||
* be specified here. It is then passed to Mongoose and can be accessed
|
||||
* globally using the model() function.
|
||||
*/
|
||||
const Greeting = {
|
||||
name: String,
|
||||
}
|
||||
|
||||
module.exports = Greeting
|
||||
14
app/models/Role.model.js
Normal file
14
app/models/Role.model.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/models/Role
|
||||
*/
|
||||
|
||||
/**
|
||||
* This model stores the role data from Flitter-auth.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
module.exports = exports = {
|
||||
name: String,
|
||||
permissions: [String],
|
||||
data: String,
|
||||
}
|
||||
20
app/models/User.model.js
Normal file
20
app/models/User.model.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/models/User
|
||||
*/
|
||||
|
||||
/**
|
||||
* This model stores the user data from Flitter-auth. The username
|
||||
* and password fields are fairly self-evident. Password hashes are
|
||||
* stored instead of cleartext. The data field is for JSON data that
|
||||
* holds various non-DB essential pieces of information about users.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
module.exports = exports = {
|
||||
username: String,
|
||||
password: String,
|
||||
data: String,
|
||||
uuid: String,
|
||||
role: String,
|
||||
permissions: [String],
|
||||
}
|
||||
13
app/models/upload/File.model.js
Normal file
13
app/models/upload/File.model.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* File Model
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const File = {
|
||||
original_name: String,
|
||||
new_name: String,
|
||||
mime: String,
|
||||
type: String,
|
||||
}
|
||||
|
||||
module.exports = exports = File
|
||||
13
app/models/v1/Out.model.js
Normal file
13
app/models/v1/Out.model.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Out Model
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const Out = {
|
||||
project_id: String,
|
||||
created: { type: Date, default: Date.now },
|
||||
brief: String,
|
||||
data: String,
|
||||
}
|
||||
|
||||
module.exports = exports = Out
|
||||
16
app/models/v1/Project.model.js
Normal file
16
app/models/v1/Project.model.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Project Model
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const uuid = require('uuid/v4')
|
||||
const Project = {
|
||||
name: String,
|
||||
user_id: String,
|
||||
archived: { type: Boolean, default: false },
|
||||
data: String,
|
||||
shared_user_ids: [String],
|
||||
uuid: { type: String, default: uuid }
|
||||
}
|
||||
|
||||
module.exports = exports = Project
|
||||
18
app/routing/Middleware.js
Normal file
18
app/routing/Middleware.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Global Middleware Definitions
|
||||
* -------------------------------------------------------------
|
||||
* These middleware are applied, in order, before every request that
|
||||
* Flitter handles, regardless of request type. Each middleware class
|
||||
* can be referenced using Flitter's global mw() function, but you can
|
||||
* also require() the class directly.
|
||||
*
|
||||
* Route-specific middleware should be specified in the corresponding
|
||||
* routes file.
|
||||
*/
|
||||
const Middleware = [
|
||||
|
||||
// mw('MiddlewareName'),
|
||||
|
||||
]
|
||||
|
||||
module.exports = exports = Middleware
|
||||
27
app/routing/middleware/HomeLogger.middleware.js
Normal file
27
app/routing/middleware/HomeLogger.middleware.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* HomeLogger Middleware
|
||||
* -------------------------------------------------------------
|
||||
* This is a sample middleware. It simply prints a console message when
|
||||
* the route that it is tied to is accessed. By default, it is called if
|
||||
* the '/' route is accessed. It can be injected in routes globally using
|
||||
* the global mw() function.
|
||||
*/
|
||||
class HomeLogger {
|
||||
|
||||
/*
|
||||
* Run the middleware test.
|
||||
* This method is required by all Flitter middleware.
|
||||
* It should either call the next function in the stack,
|
||||
* or it should handle the response accordingly.
|
||||
*/
|
||||
test(req, res, next){
|
||||
console.log("Home was accessed!")
|
||||
|
||||
/*
|
||||
* Call the next function in the stack.
|
||||
*/
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HomeLogger
|
||||
49
app/routing/middleware/auth/Permission.middleware.js
Normal file
49
app/routing/middleware/auth/Permission.middleware.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/routing/middleware/RequireAuth
|
||||
*/
|
||||
|
||||
/**
|
||||
* This middleware is provided by Flitter-auth. It will redirect the user
|
||||
* back to their previous location if the does not have the specified permission.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class Permission {
|
||||
|
||||
/**
|
||||
* Run the middleware's check. If an authenticated session exists and the user has the specified permission,
|
||||
* let the request continue. If an authenticated session doesn't exist, write the destination to the
|
||||
* session and redirect the user to the login page. If the permission doesn't exist, show a 401.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
* @param {Function} next - Express handler stack callback. This should be called if the middleware check passed to allow the request to continue.
|
||||
* @param {string} permission - Name of the permission to require
|
||||
*/
|
||||
async test(req, res, next, permission){
|
||||
if ( req.session && req.session.auth && (req.session.auth.authenticated === true || req.session.auth.user) ){
|
||||
if ( req.session.auth.user.permissions && req.session.auth.user.permissions.includes(permission) ){
|
||||
next()
|
||||
}
|
||||
else if ( req.session.auth.user.role ){
|
||||
const Role = _flitter.model('auth:Role')
|
||||
const role = await Role.findOne({name: req.session.auth.user.role})
|
||||
|
||||
if ( role.permissions.includes(permission) ){
|
||||
next()
|
||||
}
|
||||
else {
|
||||
return _flitter.error(res, 401, {reason: 'Insufficient user permissions.'})
|
||||
}
|
||||
}
|
||||
else {
|
||||
return _flitter.error(res, 401, {reason: 'Insufficient user permissions.'})
|
||||
}
|
||||
}
|
||||
else {
|
||||
req.session.destination = req.originalUrl
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Permission
|
||||
35
app/routing/middleware/auth/RequireAuth.middleware.js
Normal file
35
app/routing/middleware/auth/RequireAuth.middleware.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/routing/middleware/RequireAuth
|
||||
*/
|
||||
|
||||
/**
|
||||
* This middleware is provided by Flitter-auth. It will redirect the user
|
||||
* back to their previous location if the does not contain a user object.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class RequireAuth {
|
||||
|
||||
/**
|
||||
* Run the middleware's check. If an authenticated session exists, let the request continue.
|
||||
* If an authenticated session doesn't exist, write the destination to the session and redirect
|
||||
* the user to the login page.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
* @param {Function} next - Express handler stack callback. This should be called if the middleware check passed to allow the request to continue.
|
||||
*/
|
||||
test(req, res, next){
|
||||
if ( req.session && req.session.auth && (req.session.auth.authenticated === true || req.session.auth.user) ){
|
||||
/*
|
||||
* Call the next function in the stack.
|
||||
*/
|
||||
next()
|
||||
}
|
||||
else {
|
||||
req.session.destination = req.originalUrl
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RequireAuth
|
||||
32
app/routing/middleware/auth/RequireGuest.middleware.js
Normal file
32
app/routing/middleware/auth/RequireGuest.middleware.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/routing/middleware/RequireGuest
|
||||
*/
|
||||
|
||||
/**
|
||||
* This middleware is provided by Flitter-auth. It will redirect the user
|
||||
* back to their previous location if the session contains the user object.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class RequireGuest {
|
||||
|
||||
/**
|
||||
* Run the middleware test. If an authenticated session exists, redirect the user to an error page.
|
||||
* Otherwise, allow the request to continue.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
* @param {Function} next - The callback to continue the Express request handling stack. This is called if the middleware check passes.
|
||||
*/
|
||||
test(req, res, next){
|
||||
if ( req.session && req.session.auth && (req.session.auth.authenticated === true || req.session.auth.user) ){
|
||||
return _flitter.view(res, 'errors/requires_guest')
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the next function in the stack.
|
||||
*/
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RequireGuest
|
||||
38
app/routing/middleware/auth/Role.middleware.js
Normal file
38
app/routing/middleware/auth/Role.middleware.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/routing/middleware/RequireAuth
|
||||
*/
|
||||
|
||||
/**
|
||||
* This middleware is provided by Flitter-auth. It will redirect the user
|
||||
* back to their previous location if the does not have the specified role.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class Role {
|
||||
|
||||
/**
|
||||
* Run the middleware's check. If an authenticated session exists and the user has the specified role,
|
||||
* let the request continue. If an authenticated session doesn't exist, write the destination to the
|
||||
* session and redirect the user to the login page. If the role doesn't exist, show a 401.
|
||||
* @param {Express/Request} req - the incoming Express request
|
||||
* @param {Express/Response} res - the corresponding Express response
|
||||
* @param {Function} next - Express handler stack callback. This should be called if the middleware check passed to allow the request to continue.
|
||||
* @param {string} role - Name of the role to require
|
||||
*/
|
||||
test(req, res, next, role){
|
||||
if ( req.session && req.session.auth && (req.session.auth.authenticated === true || req.session.auth.user) ){
|
||||
if ( req.session.auth.user.role && req.session.auth.user.role === role ){
|
||||
next()
|
||||
}
|
||||
else {
|
||||
return _flitter.error(res, 401, {reason: 'Insufficient user permissions.'})
|
||||
}
|
||||
}
|
||||
else {
|
||||
req.session.destination = req.originalUrl
|
||||
return res.redirect('/auth/login')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Role
|
||||
53
app/routing/routers/api/v1.routes.js
Normal file
53
app/routing/routers/api/v1.routes.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* v1 Routes
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const v1 = {
|
||||
|
||||
/*
|
||||
* Define the prefix applied to each of these routes.
|
||||
* For example, if prefix is '/auth':
|
||||
* '/' becomes '/auth'
|
||||
* '/login' becomes '/auth/login'
|
||||
*/
|
||||
prefix: '/api/v1',
|
||||
|
||||
/*
|
||||
* Define middleware that should be applied to all
|
||||
* routes defined in this file. Middleware should be
|
||||
* included using Flitter's global mw() function, but
|
||||
* it can also be added directly using require().
|
||||
*/
|
||||
middleware: [
|
||||
// mw('Middleware Name'),
|
||||
],
|
||||
|
||||
/*
|
||||
* Define GET routes.
|
||||
* These routes are registered as GET methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
get: {
|
||||
// '/': [ controller('Controller_Name').handler_name ],
|
||||
},
|
||||
|
||||
/*
|
||||
* Define POST routes.
|
||||
* These routes are registered as POST methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
post: {
|
||||
'/out/:key': [ _flitter.controller('api:v1').new_out ],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = v1
|
||||
52
app/routing/routers/auth.routes.js
Normal file
52
app/routing/routers/auth.routes.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @module flitter-auth/deploy/routing/routers/auth
|
||||
*/
|
||||
|
||||
/**
|
||||
* These are the route definitions for Flitter-auth.
|
||||
* @type {Object}
|
||||
*/
|
||||
module.exports = exports = {
|
||||
|
||||
/*
|
||||
* Define the prefix applied to each of these routes.
|
||||
* For example, if prefix is '/auth':
|
||||
* '/' becomes '/auth'
|
||||
* '/login' becomes '/auth/login'
|
||||
*/
|
||||
prefix: '/auth',
|
||||
|
||||
/*
|
||||
* Define GET routes.
|
||||
* These routes are registered as GET methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
get: {
|
||||
'/register': [ _flitter.mw('auth:RequireGuest'), _flitter.controller('Auth').register_get ],
|
||||
'/login': [ _flitter.mw('auth:RequireGuest'), _flitter.controller('Auth').login_get ],
|
||||
'/logout': [ _flitter.mw('auth:RequireAuth'), _flitter.controller('Auth').logout ],
|
||||
|
||||
/*
|
||||
* A placeholder dashboard.
|
||||
*/
|
||||
'/dash': [ _flitter.mw('auth:RequireAuth'), _flitter.controller('Auth').dash_get ]
|
||||
},
|
||||
|
||||
/*
|
||||
* Define POST routes.
|
||||
* These routes are registered as POST methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
post: {
|
||||
'/register': [ _flitter.mw('auth:RequireGuest'), _flitter.controller('Auth').register_post ],
|
||||
'/login': [ _flitter.mw('auth:RequireGuest'), _flitter.controller('Auth').login_post ],
|
||||
},
|
||||
}
|
||||
59
app/routing/routers/dash/v1.routes.js
Normal file
59
app/routing/routers/dash/v1.routes.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* v1 Routes
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
const v1 = {
|
||||
|
||||
/*
|
||||
* Define the prefix applied to each of these routes.
|
||||
* For example, if prefix is '/auth':
|
||||
* '/' becomes '/auth'
|
||||
* '/login' becomes '/auth/login'
|
||||
*/
|
||||
prefix: '/dash/v1',
|
||||
|
||||
/*
|
||||
* Define middleware that should be applied to all
|
||||
* routes defined in this file. Middleware should be
|
||||
* included using Flitter's global mw() function, but
|
||||
* it can also be added directly using require().
|
||||
*/
|
||||
middleware: [
|
||||
// mw('Middleware Name'),
|
||||
_flitter.mw('auth:RequireAuth')
|
||||
],
|
||||
|
||||
/*
|
||||
* Define GET routes.
|
||||
* These routes are registered as GET methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
get: {
|
||||
// '/': [ controller('Controller_Name').handler_name ],
|
||||
'/': [ _flitter.controller('dash:v1').main ],
|
||||
|
||||
'/project/new': [ _flitter.controller('dash:v1').new_project_show ],
|
||||
'/project/view/:id': [ _flitter.controller('dash:v1').project_view ],
|
||||
'/out/view/:id': [ _flitter.controller('dash:v1').out_view ],
|
||||
},
|
||||
|
||||
/*
|
||||
* Define POST routes.
|
||||
* These routes are registered as POST methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
post: {
|
||||
'/project/new': [ _flitter.controller('dash:v1').new_project_do ],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = v1
|
||||
54
app/routing/routers/index.routes.js
Normal file
54
app/routing/routers/index.routes.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Index Routes
|
||||
* -------------------------------------------------------------
|
||||
* This is a sample routes file. Routes and their handlers should be
|
||||
* defined here, but no logic should occur.
|
||||
*/
|
||||
const index = {
|
||||
|
||||
/*
|
||||
* Define the prefix applied to each of these routes.
|
||||
* For example, if prefix is '/auth':
|
||||
* '/' becomes '/auth'
|
||||
* '/login' becomes '/auth/login'
|
||||
*/
|
||||
prefix: '/',
|
||||
|
||||
/*
|
||||
* Define middleware that should be applied to all
|
||||
* routes defined in this file. Middleware should be
|
||||
* included using Flitter's global mw() function, but
|
||||
* it can also be added directly using require().
|
||||
*/
|
||||
middleware: [
|
||||
// _flitter.mw('HomeLogger'),
|
||||
],
|
||||
|
||||
/*
|
||||
* Define GET routes.
|
||||
* These routes are registered as GET methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
get: {
|
||||
'/': [ _flitter.controller('Home').welcome ],
|
||||
},
|
||||
|
||||
/*
|
||||
* Define POST routes.
|
||||
* These routes are registered as POST methods.
|
||||
* Handlers for these routes should be specified as
|
||||
* an array of functions that are applied in order.
|
||||
*
|
||||
* mw() calls apply Flitter middleware
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
post: {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = index
|
||||
7
app/validators/v1/Project.validator.js
Normal file
7
app/validators/v1/Project.validator.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// Project Validator
|
||||
|
||||
const Project = {
|
||||
// field: [ 'required' ],
|
||||
}
|
||||
|
||||
module.exports = exports = Project
|
||||
111
app/views/auth/dash.pug
Normal file
111
app/views/auth/dash.pug
Normal file
@@ -0,0 +1,111 @@
|
||||
html
|
||||
head
|
||||
title Dashboard | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title Welcome, #{ user.username }!
|
||||
form.login-form(method="GET" action="/auth/logout" enctype="multipart/form-data")
|
||||
button Logout
|
||||
111
app/views/auth/logged_out.pug
Normal file
111
app/views/auth/logged_out.pug
Normal file
@@ -0,0 +1,111 @@
|
||||
html
|
||||
head
|
||||
title Good-bye! | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title You have been signed out.
|
||||
form.login-form(method="GET" action="/" enctype="multipart/form-data")
|
||||
button Click to Continue
|
||||
128
app/views/auth/login.pug
Normal file
128
app/views/auth/login.pug
Normal file
@@ -0,0 +1,128 @@
|
||||
html
|
||||
head
|
||||
title Sign-In | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 12pt;
|
||||
color: darkred;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title Sign-in to continue.
|
||||
if submission_data && submission_data.error
|
||||
each error in submission_data.errors
|
||||
p.form-error #{error}
|
||||
form.login-form(method="POST" action="/auth/login" enctype="multipart/form-data")
|
||||
if submission_data.data.username
|
||||
input(type='text' name='username' placeholder='username' value=submission_data.data.username required autofocus)
|
||||
else
|
||||
input(type='text' name='username' placeholder='username' required autofocus)
|
||||
input(type='password' name='password' placeholder='password' required)
|
||||
button login
|
||||
p.message
|
||||
| No account?
|
||||
a(href='/auth/register') Register instead
|
||||
129
app/views/auth/register.pug
Normal file
129
app/views/auth/register.pug
Normal file
@@ -0,0 +1,129 @@
|
||||
html
|
||||
head
|
||||
title Register | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 12pt;
|
||||
color: darkred;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title Register to continue.
|
||||
if submission_data && submission_data.error
|
||||
each error in submission_data.errors
|
||||
p.form-error #{error}
|
||||
form.login-form(method="POST" action="/auth/register" enctype="multipart/form-data")
|
||||
if submission_data.data.username
|
||||
input(type='text' name='username' placeholder='username' value=submission_data.data.username required autofocus)
|
||||
else
|
||||
input(type='text' name='username' placeholder='username' required autofocus)
|
||||
input(type='password' name='password' placeholder='password' required)
|
||||
input(type='password' name='password_verify' placeholder='confirm password' required)
|
||||
button login
|
||||
p.message
|
||||
| Already registered?
|
||||
a(href='/auth/login') Sign-in instead
|
||||
111
app/views/auth/register_success.pug
Normal file
111
app/views/auth/register_success.pug
Normal file
@@ -0,0 +1,111 @@
|
||||
html
|
||||
head
|
||||
title Register | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title Registration successful!
|
||||
form.login-form(method="GET" action="/" enctype="multipart/form-data")
|
||||
button Click to Continue
|
||||
111
app/views/auth/requires_guest.pug
Normal file
111
app/views/auth/requires_guest.pug
Normal file
@@ -0,0 +1,111 @@
|
||||
html
|
||||
head
|
||||
title Register | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title Registration successful!
|
||||
form.login-form(method="GET" action="/" enctype="multipart/form-data")
|
||||
button Click to Continue
|
||||
26
app/views/dash_v1/main.pug
Normal file
26
app/views/dash_v1/main.pug
Normal file
@@ -0,0 +1,26 @@
|
||||
html
|
||||
head
|
||||
title DevBug Dashboard
|
||||
link(rel='stylesheet' href='/assets/dash_v1.css')
|
||||
body
|
||||
h1 DevBug Dashboard
|
||||
ul(style='list-style-type: none; display: inline; margin: 0; padding: 0; padding-right: 15px;')
|
||||
li
|
||||
a(href='/auth/logout') Logout
|
||||
h3 My Projects
|
||||
ul(style='list-style-type: none;')
|
||||
li
|
||||
a(href='/dash/v1/project/new') Create New Project
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th(scope='col' style='min-width: 250px') Name
|
||||
th(scope='col') Actions
|
||||
tbody
|
||||
each project in projects
|
||||
tr
|
||||
td #{project.name}
|
||||
td
|
||||
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
||||
li
|
||||
a.action(href='/dash/v1/project/view/'+project.id) View
|
||||
10
app/views/dash_v1/out.pug
Normal file
10
app/views/dash_v1/out.pug
Normal file
@@ -0,0 +1,10 @@
|
||||
html
|
||||
head
|
||||
title #{out.brief}
|
||||
body
|
||||
h2 #{out.brief}
|
||||
div
|
||||
a(href='javascript:window.history.back()') Back
|
||||
pre
|
||||
code
|
||||
div #{prettyd}
|
||||
14
app/views/dash_v1/project.pug
Normal file
14
app/views/dash_v1/project.pug
Normal file
@@ -0,0 +1,14 @@
|
||||
html
|
||||
head
|
||||
title #{(update ? 'Update Project' : 'Create New Project')} | DevBug
|
||||
body
|
||||
h2 #{(update ? 'Update Project' : 'Create New Project')}
|
||||
if errors
|
||||
each error in errors
|
||||
p(style='color: red; font-weight: bold;') #{error}
|
||||
form(method='post', enctype='multipart/form-data')
|
||||
label(for='project_name') Project Name:
|
||||
input#project_name(type='text', name='name' required autofocus)
|
||||
br
|
||||
br
|
||||
button(type='submit') Create Project
|
||||
27
app/views/dash_v1/view.pug
Normal file
27
app/views/dash_v1/view.pug
Normal file
@@ -0,0 +1,27 @@
|
||||
html
|
||||
head
|
||||
title View: #{project.name} | Devbug
|
||||
link(rel='stylesheet' href='/assets/dash_v1.css')
|
||||
body
|
||||
h2 View: #{project.name}
|
||||
h4 API Key: #{ project.uuid }
|
||||
ul(style='list-style-type: none; display: inline; margin: 0; padding: 0; padding-right: 15px;')
|
||||
li
|
||||
a(href='/dash/v1') Dashboard
|
||||
li
|
||||
a(href='/auth/logout') Logout
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th(scope='col' style='min-width: 250px') Brief
|
||||
th(scope='col' style='min-width: 250px') Created On
|
||||
th(scope='col') Actions
|
||||
tbody
|
||||
each out in outs
|
||||
tr
|
||||
td #{out.brief}
|
||||
td #{ out.created.toLocaleString({timeZone: 'America/Chicago'}) }
|
||||
td
|
||||
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
||||
li
|
||||
a.action(href='/dash/v1/out/view/'+out.id) View
|
||||
33
app/views/errors/404.pug
Normal file
33
app/views/errors/404.pug
Normal file
@@ -0,0 +1,33 @@
|
||||
html
|
||||
head
|
||||
title Not Found | Flitter
|
||||
style(type="text/css").
|
||||
@import url('https://fonts.googleapis.com/css?family=Rajdhani');
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
background-color: #c7dbdf;
|
||||
}
|
||||
|
||||
.flitter-container {
|
||||
height: 60%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flitter-image {
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.flitter-name {
|
||||
font-family: "Rajdhani";
|
||||
font-size: 50pt;
|
||||
margin-left: 35px;
|
||||
color: #00323d;
|
||||
}
|
||||
body
|
||||
.flitter-container
|
||||
img.flitter-image(src="/assets/flitter.png")
|
||||
p.flitter-name 404: Page Not Found
|
||||
33
app/views/errors/500.pug
Normal file
33
app/views/errors/500.pug
Normal file
@@ -0,0 +1,33 @@
|
||||
html
|
||||
head
|
||||
title Server Error | Flitter
|
||||
style(type="text/css").
|
||||
@import url('https://fonts.googleapis.com/css?family=Rajdhani');
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
background-color: #c7dbdf;
|
||||
}
|
||||
|
||||
.flitter-container {
|
||||
height: 60%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flitter-image {
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.flitter-name {
|
||||
font-family: "Rajdhani";
|
||||
font-size: 50pt;
|
||||
margin-left: 35px;
|
||||
color: #00323d;
|
||||
}
|
||||
body
|
||||
.flitter-container
|
||||
img.flitter-image(src="/assets/flitter.png")
|
||||
p.flitter-name 500: Internal Server Error
|
||||
39
app/views/errors/development.pug
Normal file
39
app/views/errors/development.pug
Normal file
@@ -0,0 +1,39 @@
|
||||
html
|
||||
head
|
||||
title Uh-Oh! | Flitter
|
||||
style(type="text/css").
|
||||
@import url('https://fonts.googleapis.com/css?family=Rajdhani');
|
||||
@import url('https://fonts.googleapis.com/css?family=Oxygen+Mono');
|
||||
html,
|
||||
body {
|
||||
background-color: #c7dbdf;
|
||||
font-family: "Rajdhani";
|
||||
padding-left: 2%;
|
||||
padding-top: 2%;
|
||||
}
|
||||
p {
|
||||
font-family: "Oxygen Mono";
|
||||
font-size: 14pt;
|
||||
}
|
||||
body
|
||||
h1 Error: #{error.message}
|
||||
h3 Status: #{error.status}
|
||||
h4#errmsg
|
||||
p !{error.stack.replace(/\n/g, '<br>')}
|
||||
|
||||
script.
|
||||
const errors = [
|
||||
'Insert your Windows installation disc and restart your computer.',
|
||||
'I am a teapot.',
|
||||
'Printing not supported on this printer.',
|
||||
'Keyboard not found. Press F1 to continue.',
|
||||
'Bailing out. You\'re on your own. Good luck.',
|
||||
'A team of highly trained monkeys is on its way.',
|
||||
'Well.... something happened.',
|
||||
'Beats the hell out of me, but something went wrong.',
|
||||
'Yeaaaaah... if you could, like, not, that\'d be great.',
|
||||
'I\'m fine. Everything is fine.',
|
||||
'Blocked by Windows Parental Controls.'
|
||||
]
|
||||
|
||||
document.getElementById('errmsg').innerHTML = errors[Math.floor(Math.random()*errors.length)]
|
||||
111
app/views/errors/requires_guest.pug
Normal file
111
app/views/errors/requires_guest.pug
Normal file
@@ -0,0 +1,111 @@
|
||||
html
|
||||
head
|
||||
title Uh, oh! | Flitter
|
||||
style(type="text/css").
|
||||
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
|
||||
|
||||
.login-page {
|
||||
width: 360px;
|
||||
padding: 8% 0 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #FFFFFF;
|
||||
max-width: 360px;
|
||||
margin: 0 auto 100px;
|
||||
padding: 45px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.form input {
|
||||
font-family: "Roboto", sans-serif;
|
||||
outline: 0;
|
||||
background: #f2f2f2;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0 0 15px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: #006a81;
|
||||
}
|
||||
|
||||
.form button {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-transform: uppercase;
|
||||
outline: 0;
|
||||
background: #006a81;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 15px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
-webkit-transition: background 0.6s;
|
||||
-moz-transition: background 0.6s;
|
||||
-ms-transition: background 0.6s;
|
||||
-o-transition: background 0.6s;
|
||||
transition: background 0.6s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form button:hover, .form button:active, .form button:focus {
|
||||
background: #00b3da;
|
||||
}
|
||||
|
||||
.form .message {
|
||||
margin: 15px 0 0;
|
||||
color: #b3b3b3;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form .message a {
|
||||
color: #006a81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.container .info h1 {
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.container .info span {
|
||||
color: #4d4d4d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.container .info span a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #c7dbdf;
|
||||
font-family: "Roboto", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.flitter-logo {
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16pt;
|
||||
color: #006a81;
|
||||
}
|
||||
body
|
||||
.login-page
|
||||
.form
|
||||
img.flitter-logo(src="/assets/flitter.png")
|
||||
p.form-title This area is off limits.
|
||||
form.login-form(method="GET" action="/" enctype="multipart/form-data")
|
||||
button Click to Continue
|
||||
40
app/views/welcome.pug
Normal file
40
app/views/welcome.pug
Normal file
@@ -0,0 +1,40 @@
|
||||
html
|
||||
head
|
||||
title Flitter
|
||||
style(type="text/css").
|
||||
@import url('https://fonts.googleapis.com/css?family=Rajdhani');
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
background-color: #c7dbdf;
|
||||
}
|
||||
|
||||
.flitter-container {
|
||||
height: 60%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flitter-container-slim {
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flitter-image {
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.flitter-name {
|
||||
font-family: "Rajdhani";
|
||||
font-size: 50pt;
|
||||
margin-left: 35px;
|
||||
color: #00323d;
|
||||
}
|
||||
body
|
||||
.flitter-container
|
||||
img.flitter-image(src="/assets/flitter.png")
|
||||
p.flitter-name powered by flitter
|
||||
Reference in New Issue
Block a user