master
Garrett Mills 5 years ago
commit 487f0c4eeb

90
.gitignore vendored

@ -0,0 +1,90 @@
# Created by https://www.gitignore.io/api/node
# Edit at https://www.gitignore.io/?templates=node
.idea
.idea/*
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# End of https://www.gitignore.io/api/node

@ -0,0 +1,7 @@
Copyright 2019 Garrett L. Mills.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,72 @@
<p align="center"><img height="200" src="https://static.glmdev.tech/flitter/flitter-big.png"></p>
# Flitter
Flitter is a quick & ligthweight web app framework based on Express.
### What?
Flitter is an MVC style framework that aims to get you up and running faster by providing a structure and a wrapper for Express.js. Files in predictable directories are parsed into routes, middleware, controllers, models, and views.
Flitter provides access to the Express app, while making it possible to create an app without needing to piece together the Express framework.
### Flitter Provides:
- Express for routing
- Mongoose for ODM
- Busboy for request parsing
- Favicon support
- `./flitter` - CLI tools for Flitter (including an interactive shell)
- User auth & sessions (see below)
### How?
Getting started with Flitter is easy. To create a new project, simply run the following commands:
```
# Download Flitter:
git clone https://git.glmdev.tech/flitter/flitter {project_name}
cd {project_name}
# Install dependencies:
yarn install
# Create default config:
cp example.env .env
# Launch Flitter!
./flitter up
```
And voilà! You should have a Flitter app up and running on port `8000` by default.
### Why?
Flitter's creator is a former Laravel junkie, but loves Node and Express. He got tired of having to hammer out the same 500 lines of code to start every project, but didn't want the bulk and obfuscation of larger frameworks like AdonisJS.
Flitter is designed to be compartmentalized and easy to understand. Every piece of its core functionality is broken into "units." Each of these units does some task like loading config, parsing middleware, connecting to the database, etc. You can see exactly what units your application is loading by viewing the Units file in `config/Units.flitter.js`. Each of Flitters core units are open to view in the [libflitter](https://www.npmjs.com/package/libflitter) package.
Of course, this also means that Flitter is extremely easy to extend. If you want to add a custom package, simply require it and add its unit to the Units file!
### Who?
Flitter was created by [Garrett Mills](https://glmdev.tech/), and its use is governed by the terms of the MIT License as specified in the LICENSE file.
Of course, that does mean that Flitter is © 2019 Garrett Mills. ;)
#### Auth?
Out of the box, Flitter ships with a ready-to-use user registration/login/session system called [flitter-auth](https://git.glmdev.tech/flitter/auth). Flitter Auth provides:
- Registration & Login
- RequireAuth and RequireGuest route middlewares
- A customizable User model w/ Bcrypt hashed passwords
- User session support with logout functions
To get started using Flitter Auth, just run:
```
./flitter deploy auth
```
This command will copy the necessary files to your Flitter install. The files are directly accessible and, therefore, completely customizable.

@ -0,0 +1,97 @@
/*
* The Flitter Units File
* -------------------------------------------------------------
* Flitter uses a unit-chain style initialization system. This means that
* individual components of Flitter and its add-ons are specified in order
* here. Then, when the app is created, Flitter creates a single functional
* chain by passing the next unit to the current unit's loading script. This
* launches Flitter with a single function call (FlitterApp.up()) and enables
* developers to contextualize Flitter within async or callback functions.
*/
const FlitterUnits = {
/*
* The Core Flitter Units
* -------------------------------------------------------------
* These units comprise the core functionality of Flitter. Unless you
* really know what you are doing, you should NEVER change them.
*/
'Config' : new (require('libflitter/config/ConfigUnit'))(),
'Utility' : new (require('libflitter/utility/UtilityUnit'))(),
'Database' : new (require('libflitter/database/DatabaseUnit'))(),
'Express' : new (require('libflitter/express/ExpressUnit'))(),
'ViewEngine' : new (require('libflitter/views/ViewEngineUnit'))(),
/*
* Pre-Routing Custom Units
* -------------------------------------------------------------
* Custom units that modify or add functionality that needs to be made
* available to the middleware-routing-controller stack.
*/
'Upload' : new (require('flitter-upload/UploadUnit'))(),
/*
* The Core Flitter Units
* -------------------------------------------------------------
* These units comprise the core functionality of Flitter. Unless you
* really know what you are doing, you should NEVER change them.
*/
'Middleware' : new (require('libflitter/middleware/MiddlewareUnit'))(),
'Controller' : new (require('libflitter/controller/ControllerUnit'))(),
'Routing' : new (require('libflitter/routing/RoutingUnit'))(),
'Static' : new (require('libflitter/static/StaticUnit'))(),
/*
* The Misc Unit
* -------------------------------------------------------------
* This unit loads changes and resources customized by the application.
*/
'Misc' : new (require('./app/MiscUnit'))(),
/*
* Secondary Flitter Units
* -------------------------------------------------------------
* These units aren't strictly required for the core functionality of
* Flitter, but they enable the use of certain Flitter tools, like the
* ./flitter command and its handlers.
*/
'Cli' : new (require('flitter-cli/CliUnit'))(),
'Forms' : new (require('flitter-forms/FormsUnit'))(),
'Auth' : new (require('flitter-auth/AuthUnit'))(),
'Flap' : new (require('flitter-flap/FlapUnit'))(),
/*
* Custom Flitter Units
* -------------------------------------------------------------
* Custom units should be specified here. They will be loaded in order
* after the core of Flitter has been initialized.
*/
// 'CustomUnit' : new CustomUnit(),
/*
* HTTP Error Handling
* -------------------------------------------------------------
* This unit provides default routes for invalid requests and tags them as
* 404 errors. It also provides middleware to display error views according
* to their HTTP status code. This allows for custom views which are located
* in views/errors/.
*/
'Error' : new (require('libflitter/errors/ErrorUnit'))(),
/*
* The Flitter App Unit
* -------------------------------------------------------------
* This should ALWAYS be the last unit to run. The app unit contains the
* call to the Node HTTP server that launches Flitter. Units listed after
* the app unit are in an lower context than the Flitter app and therefore
* won't be available to Flitter or the underlying Express framework.
*/
'App' : new (require('libflitter/app/AppUnit'))(),
}
module.exports = exports = FlitterUnits

@ -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,0 +1,10 @@
table, th, td {
border: 1px solid #cccccc;
}
th, td {
padding-top: 5px;
padding-bottom: 5px;
padding-left: 10px;
padding-right: 10px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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,
}

@ -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],
}

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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 ],
},
}

@ -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

@ -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

@ -0,0 +1,7 @@
// Project Validator
const Project = {
// field: [ 'required' ],
}
module.exports = exports = Project

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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}

@ -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

@ -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

@ -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

@ -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

@ -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)]

@ -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

@ -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

@ -0,0 +1,15 @@
const database_config = {
name: process.env.DATABASE_NAME || 'feather',
host: process.env.DATABASE_HOST || 'localhost',
port: process.env.DATABASE_PORT || '27017',
auth: {
require: process.env.DATABASE_AUTH || false,
username: process.env.DATABASE_USERNAME || '',
password: process.env.DATABASE_PASSWORD || '',
},
}
module.exports = database_config

@ -0,0 +1,17 @@
const server_config = {
port: process.env.SERVER_PORT || 80,
environment: process.env.ENVIRONMENT || "production",
logging: {
level: process.env.LOGGING_LEVEL || 1
},
session: {
secret: process.env.SECRET || "changeme"
},
uploads: {
destination: './uploads'
}
}
module.exports = server_config

@ -0,0 +1,21 @@
version: "3"
services:
db:
image: mongo:latest
container_name: db
ports:
- 27017:27017
volumes:
- dbdata1:/data
web:
image: node:11.14-stretch
command: bash -c "cd /opt/flitterapp && yarn install && ./flitter up"
depends_on:
- db
ports:
- 8000:8000
volumes:
- ./:/opt/flitterapp
volumes:
dbdata1:
driver: local

@ -0,0 +1,10 @@
SERVER_PORT=8000
LOGGING_LEVEL=1
DATABASE_HOST=db
DATABASE_PORT=27017
DATABASE_NAME=flitter
DATABASE_AUTH=false
SECRET=changeme
ENVIRONMENT=production

@ -0,0 +1,10 @@
SERVER_PORT=8000
LOGGING_LEVEL=1
DATABASE_HOST=127.0.0.1
DATABASE_PORT=27017
DATABASE_NAME=flitter
DATABASE_AUTH=false
SECRET=changeme
ENVIRONMENT=production

@ -0,0 +1,22 @@
[
{
"id": 1555000000,
"name": "create_flaps_json_file",
"migratedOn": "2019-04-12T17:26:34.522Z"
},
{
"id": 1555611659,
"name": "move_views_dir_inside_app_dir",
"migratedOn": "2019-04-18T19:24:16.741Z"
},
{
"id": 1556469759,
"name": "create_docker_env_file",
"migratedOn": "2019-04-28T16:49:11.238Z"
},
{
"id": 1560988609,
"name": "move_database_unit_before_express_unit",
"migratedOn": "2019-06-21T00:31:38.019Z"
}
]

@ -0,0 +1,20 @@
#!/usr/bin/env node
/*
* ./flitter
* -------------------------------------------------------------
* The ./flitter command is used to interact with Flitter and its tools
* in the development environment. Currently, it lends access to Flitter
* shell, which is like a Node interactive prompt, but it's launched from
* within the same context as the Flitter HTTP server, allowing developers
* to interact with Flitter directly.
*/
const CliAppUnit = require('flitter-cli/CliAppUnit')
const units = require('./Units.flitter')
units['App'] = new CliAppUnit()
const FlitterApp = require('libflitter/app/FlitterApp')
const flitter = new FlitterApp(units)
flitter.up()

@ -0,0 +1,27 @@
/*
* Load the units file.
* -------------------------------------------------------------
* This file contains an ordered object of unit files. Flitter will load these
* one at a time to launch the application. Each unit in the sequence is passed
* the function for the next unit in the sequence. This forms the function stack
* by chaining the units together, ending with the Flitter App unit.
*/
const units = require('./Units.flitter')
/*
* Create the app.
* -------------------------------------------------------------
* The FlitterApp object contains the wrapper for the Express app, as well as
* the initialization function that chains together the individual units. This
* is why we pass it the units.
*/
const FlitterApp = require('libflitter/app/FlitterApp')
const flitter = new FlitterApp(units)
/*
* Launch the server.
* -------------------------------------------------------------
* This calls the first unit in the unit chain. This chain ends with the Flitter
* server component which launches the Node HTTP server.
*/
flitter.up()

@ -0,0 +1,27 @@
{
"name": "flitter",
"version": "0.1.0",
"description": "Flitter is a simple MVC framework wrapper for Express.js.",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://git.glmdev.tech/flitter/flitter"
},
"keywords": [
"flitter",
"glmdev",
"framework",
"express"
],
"author": "Garrett Mills <garrett@glmdev.tech> (https://glmdev.tech/)",
"license": "MIT",
"dependencies": {
"flitter-auth": "^0.4.0",
"flitter-cli": "^0.10.0",
"flitter-flap": "^0.2.2",
"flitter-forms": "^0.7.2",
"flitter-upload": "^0.7.6",
"libflitter": "^0.27.4",
"stringify-object": "^3.3.0"
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save