DataInventory

chore: init commit

1/13/2020 10:16:33 AM

Changes

.editorconfig 13(+13 -0)

.env.example 18(+18 -0)

.gitignore 16(+16 -0)

ace 21(+21 -0)

app/Models/User.js 39(+39 -0)

config/app.js 243(+243 -0)

config/auth.js 94(+94 -0)

config/bodyParser.js 157(+157 -0)

config/cors.js 87(+87 -0)

config/database.js 81(+81 -0)

config/hash.js 49(+49 -0)

config/session.js 101(+101 -0)

config/shield.js 145(+145 -0)

database/factory.js 21(+21 -0)

package.json 35(+35 -0)

public/logo.svg 1(+1 -0)

public/style.css 92(+92 -0)

public/title.svg 1(+1 -0)

README.md 31(+31 -0)

server.js 25(+25 -0)

start/app.js 61(+61 -0)

start/kernel.js 63(+63 -0)

start/routes.js 20(+20 -0)

Details

.editorconfig 13(+13 -0)

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..9142239
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# editorconfig.org
+root = true
+
+[*]
+indent_size = 2
+indent_style = space
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false

.env.example 18(+18 -0)

diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..6a2fb47
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,18 @@
+HOST=127.0.0.1
+PORT=3333
+NODE_ENV=development
+APP_URL=http://${HOST}:${PORT}
+
+CACHE_VIEWS=false
+
+APP_KEY=
+
+DB_CONNECTION=sqlite
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_USER=root
+DB_PASSWORD=
+DB_DATABASE=adonis
+
+SESSION_DRIVER=cookie
+HASH_DRIVER=bcrypt

.gitignore 16(+16 -0)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..585b363
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+# Node modules
+node_modules
+package-lock.json
+
+# Adonis directory for storing tmp files
+tmp
+
+# Environment variables, never commit this file
+.env
+
+# The development sqlite file
+database/*.sqlite
+
+# VSCode & Webstorm history directories
+.history
+.idea

ace 21(+21 -0)

diff --git a/ace b/ace
new file mode 100644
index 0000000..271a604
--- /dev/null
+++ b/ace
@@ -0,0 +1,21 @@
+'use strict'
+
+/*
+|--------------------------------------------------------------------------
+| Ace Commands
+|--------------------------------------------------------------------------
+|
+| The ace file is just a regular Javascript file but with no extension. You
+| can call `node ace` followed by the command name and it just works.
+|
+| Also you can use `adonis` followed by the command name, since the adonis
+| global proxy all the ace commands.
+|
+*/
+
+const { Ignitor } = require('@adonisjs/ignitor')
+
+new Ignitor(require('@adonisjs/fold'))
+  .appRoot(__dirname)
+  .fireAce()
+  .catch(console.error)
diff --git a/app/Middleware/ConvertEmptyStringsToNull.js b/app/Middleware/ConvertEmptyStringsToNull.js
new file mode 100644
index 0000000..a5750cc
--- /dev/null
+++ b/app/Middleware/ConvertEmptyStringsToNull.js
@@ -0,0 +1,17 @@
+'use strict'
+
+class ConvertEmptyStringsToNull {
+  async handle ({ request }, next) {
+    if (Object.keys(request.body).length) {
+      request.body = Object.assign(
+        ...Object.keys(request.body).map(key => ({
+          [key]: request.body[key] !== '' ? request.body[key] : null
+        }))
+      )
+    }
+
+    await next()
+  }
+}
+
+module.exports = ConvertEmptyStringsToNull
diff --git a/app/Models/Token.js b/app/Models/Token.js
new file mode 100644
index 0000000..e089e87
--- /dev/null
+++ b/app/Models/Token.js
@@ -0,0 +1,9 @@
+'use strict'
+
+/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
+const Model = use('Model')
+
+class Token extends Model {
+}
+
+module.exports = Token
diff --git a/app/Models/Traits/NoTimestamp.js b/app/Models/Traits/NoTimestamp.js
new file mode 100644
index 0000000..58c9340
--- /dev/null
+++ b/app/Models/Traits/NoTimestamp.js
@@ -0,0 +1,16 @@
+'use strict'
+
+class NoTimestamp {
+  register (Model) {
+    Object.defineProperties(Model, {
+      createdAtColumn: {
+        get: () => null,
+      },
+      updatedAtColumn: {
+        get: () => null,
+      },
+    })
+  }
+}
+
+module.exports = NoTimestamp

app/Models/User.js 39(+39 -0)

diff --git a/app/Models/User.js b/app/Models/User.js
new file mode 100644
index 0000000..2804a44
--- /dev/null
+++ b/app/Models/User.js
@@ -0,0 +1,39 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Hash')} */
+const Hash = use('Hash')
+
+/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
+const Model = use('Model')
+
+class User extends Model {
+  static boot () {
+    super.boot()
+
+    /**
+     * A hook to hash the user password before saving
+     * it to the database.
+     */
+    this.addHook('beforeSave', async (userInstance) => {
+      if (userInstance.dirty.password) {
+        userInstance.password = await Hash.make(userInstance.password)
+      }
+    })
+  }
+
+  /**
+   * A relationship on tokens is required for auth to
+   * work. Since features like `refreshTokens` or
+   * `rememberToken` will be saved inside the
+   * tokens table.
+   *
+   * @method tokens
+   *
+   * @return {Object}
+   */
+  tokens () {
+    return this.hasMany('App/Models/Token')
+  }
+}
+
+module.exports = User

config/app.js 243(+243 -0)

diff --git a/config/app.js b/config/app.js
new file mode 100644
index 0000000..42c63a3
--- /dev/null
+++ b/config/app.js
@@ -0,0 +1,243 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Env')} */
+const Env = use('Env')
+
+module.exports = {
+
+  /*
+  |--------------------------------------------------------------------------
+  | Application Name
+  |--------------------------------------------------------------------------
+  |
+  | This value is the name of your application and can be used when you
+  | need to place the application's name in a email, view or
+  | other location.
+  |
+  */
+
+  name: Env.get('APP_NAME', 'AdonisJs'),
+
+  /*
+  |--------------------------------------------------------------------------
+  | App Key
+  |--------------------------------------------------------------------------
+  |
+  | App key is a randomly generated 16 or 32 characters long string required
+  | to encrypted cookies, sessions and other sensitive data.
+  |
+  */
+  appKey: Env.getOrFail('APP_KEY'),
+
+  http: {
+    /*
+    |--------------------------------------------------------------------------
+    | Allow Method Spoofing
+    |--------------------------------------------------------------------------
+    |
+    | Method spoofing allows you to make requests by spoofing the http verb.
+    | Which means you can make a GET request but instruct the server to
+    | treat as a POST or PUT request. If you want this feature, set the
+    | below value to true.
+    |
+    */
+    allowMethodSpoofing: true,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Trust Proxy
+    |--------------------------------------------------------------------------
+    |
+    | Trust proxy defines whether X-Forwarded-* headers should be trusted or not.
+    | When your application is behind a proxy server like nginx, these values
+    | are set automatically and should be trusted. Apart from setting it
+    | to true or false Adonis supports a handful of ways to allow proxy
+    | values. Read documentation for that.
+    |
+    */
+    trustProxy: false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Subdomains
+    |--------------------------------------------------------------------------
+    |
+    | Offset to be used for returning subdomains for a given request. For
+    | majority of applications it will be 2, until you have nested
+    | sudomains.
+    | cheatsheet.adonisjs.com      - offset - 2
+    | virk.cheatsheet.adonisjs.com - offset - 3
+    |
+    */
+    subdomainOffset: 2,
+
+    /*
+    |--------------------------------------------------------------------------
+    | JSONP Callback
+    |--------------------------------------------------------------------------
+    |
+    | Default jsonp callback to be used when callback query string is missing
+    | in request url.
+    |
+    */
+    jsonpCallback: 'callback',
+
+
+    /*
+    |--------------------------------------------------------------------------
+    | Etag
+    |--------------------------------------------------------------------------
+    |
+    | Set etag on all HTTP responses. In order to disable for selected routes,
+    | you can call the `response.send` with an options object as follows.
+    |
+    | response.send('Hello', { ignoreEtag: true })
+    |
+    */
+    etag: false
+  },
+
+  views: {
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Views
+    |--------------------------------------------------------------------------
+    |
+    | Define whether or not to cache the compiled view. Set it to true in
+    | production to optimize view loading time.
+    |
+    */
+    cache: Env.get('CACHE_VIEWS', true)
+  },
+
+  static: {
+    /*
+    |--------------------------------------------------------------------------
+    | Dot Files
+    |--------------------------------------------------------------------------
+    |
+    | Define how to treat dot files when trying to serve static resources.
+    | By default it is set to ignore, which will pretend that dotfiles
+    | do not exist.
+    |
+    | Can be one of the following
+    | ignore, deny, allow
+    |
+    */
+    dotfiles: 'ignore',
+
+    /*
+    |--------------------------------------------------------------------------
+    | ETag
+    |--------------------------------------------------------------------------
+    |
+    | Enable or disable etag generation
+    |
+    */
+    etag: true,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Extensions
+    |--------------------------------------------------------------------------
+    |
+    | Set file extension fallbacks. When set, if a file is not found, the given
+    | extensions will be added to the file name and search for. The first
+    | that exists will be served. Example: ['html', 'htm'].
+    |
+    */
+    extensions: false
+  },
+
+  locales: {
+    /*
+    |--------------------------------------------------------------------------
+    | Loader
+    |--------------------------------------------------------------------------
+    |
+    | The loader to be used for fetching and updating locales. Below is the
+    | list of available options.
+    |
+    | file, database
+    |
+    */
+    loader: 'file',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Locale
+    |--------------------------------------------------------------------------
+    |
+    | Default locale to be used by Antl provider. You can always switch drivers
+    | in runtime or use the official Antl middleware to detect the driver
+    | based on HTTP headers/query string.
+    |
+    */
+    locale: 'en'
+  },
+
+  logger: {
+    /*
+    |--------------------------------------------------------------------------
+    | Transport
+    |--------------------------------------------------------------------------
+    |
+    | Transport to be used for logging messages. You can have multiple
+    | transports using same driver.
+    |
+    | Available drivers are: `file` and `console`.
+    |
+    */
+    transport: 'console',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Console Transport
+    |--------------------------------------------------------------------------
+    |
+    | Using `console` driver for logging. This driver writes to `stdout`
+    | and `stderr`
+    |
+    */
+    console: {
+      driver: 'console',
+      name: 'adonis-app',
+      level: 'info'
+    },
+
+    /*
+    |--------------------------------------------------------------------------
+    | File Transport
+    |--------------------------------------------------------------------------
+    |
+    | File transport uses file driver and writes log messages for a given
+    | file inside `tmp` directory for your app.
+    |
+    | For a different directory, set an absolute path for the filename.
+    |
+    */
+    file: {
+      driver: 'file',
+      name: 'adonis-app',
+      filename: 'adonis.log',
+      level: 'info'
+    }
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Generic Cookie Options
+  |--------------------------------------------------------------------------
+  |
+  | The following cookie options are generic settings used by AdonisJs to create
+  | cookies. However, some parts of the application like `sessions` can have
+  | seperate settings for cookies inside `config/session.js`.
+  |
+  */
+  cookie: {
+    httpOnly: true,
+    sameSite: false,
+    path: '/',
+    maxAge: 7200
+  }
+}

config/auth.js 94(+94 -0)

diff --git a/config/auth.js b/config/auth.js
new file mode 100644
index 0000000..5fceb35
--- /dev/null
+++ b/config/auth.js
@@ -0,0 +1,94 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Env')} */
+const Env = use('Env')
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | Authenticator
+  |--------------------------------------------------------------------------
+  |
+  | Authentication is a combination of serializer and scheme with extra
+  | config to define on how to authenticate a user.
+  |
+  | Available Schemes - basic, session, jwt, api
+  | Available Serializers - lucid, database
+  |
+  */
+  authenticator: 'session',
+
+  /*
+  |--------------------------------------------------------------------------
+  | Session
+  |--------------------------------------------------------------------------
+  |
+  | Session authenticator makes use of sessions to authenticate a user.
+  | Session authentication is always persistent.
+  |
+  */
+  session: {
+    serializer: 'lucid',
+    model: 'App/Models/User',
+    scheme: 'session',
+    uid: 'email',
+    password: 'password'
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Basic Auth
+  |--------------------------------------------------------------------------
+  |
+  | The basic auth authenticator uses basic auth header to authenticate a
+  | user.
+  |
+  | NOTE:
+  | This scheme is not persistent and users are supposed to pass
+  | login credentials on each request.
+  |
+  */
+  basic: {
+    serializer: 'lucid',
+    model: 'App/Models/User',
+    scheme: 'basic',
+    uid: 'email',
+    password: 'password'
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Jwt
+  |--------------------------------------------------------------------------
+  |
+  | The jwt authenticator works by passing a jwt token on each HTTP request
+  | via HTTP `Authorization` header.
+  |
+  */
+  jwt: {
+    serializer: 'lucid',
+    model: 'App/Models/User',
+    scheme: 'jwt',
+    uid: 'email',
+    password: 'password',
+    options: {
+      secret: Env.get('APP_KEY')
+    }
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Api
+  |--------------------------------------------------------------------------
+  |
+  | The Api scheme makes use of API personal tokens to authenticate a user.
+  |
+  */
+  api: {
+    serializer: 'lucid',
+    model: 'App/Models/User',
+    scheme: 'api',
+    uid: 'email',
+    password: 'password'
+  }
+}

config/bodyParser.js 157(+157 -0)

diff --git a/config/bodyParser.js b/config/bodyParser.js
new file mode 100644
index 0000000..6b40f1a
--- /dev/null
+++ b/config/bodyParser.js
@@ -0,0 +1,157 @@
+'use strict'
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | JSON Parser
+  |--------------------------------------------------------------------------
+  |
+  | Below settings are applied when request body contains JSON payload. If
+  | you want body parser to ignore JSON payload, then simply set `types`
+  | to an empty array.
+  */
+  json: {
+    /*
+    |--------------------------------------------------------------------------
+    | limit
+    |--------------------------------------------------------------------------
+    |
+    | Defines the limit of JSON that can be sent by the client. If payload
+    | is over 1mb it will not be processed.
+    |
+    */
+    limit: '1mb',
+
+    /*
+    |--------------------------------------------------------------------------
+    | strict
+    |--------------------------------------------------------------------------
+    |
+    | When `scrict` is set to true, body parser will only parse Arrays and
+    | Object. Otherwise everything parseable by `JSON.parse` is parsed.
+    |
+    */
+    strict: true,
+
+    /*
+    |--------------------------------------------------------------------------
+    | types
+    |--------------------------------------------------------------------------
+    |
+    | Which content types are processed as JSON payloads. You are free to
+    | add your own types here, but the request body should be parseable
+    | by `JSON.parse` method.
+    |
+    */
+    types: [
+      'application/json',
+      'application/json-patch+json',
+      'application/vnd.api+json',
+      'application/csp-report'
+    ]
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Raw Parser
+  |--------------------------------------------------------------------------
+  |
+  |
+  |
+  */
+  raw: {
+    types: [
+      'text/*'
+    ]
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Form Parser
+  |--------------------------------------------------------------------------
+  |
+  |
+  |
+  */
+  form: {
+    types: [
+      'application/x-www-form-urlencoded'
+    ]
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Files Parser
+  |--------------------------------------------------------------------------
+  |
+  |
+  |
+  */
+  files: {
+    types: [
+      'multipart/form-data'
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Max Size
+    |--------------------------------------------------------------------------
+    |
+    | Below value is the max size of all the files uploaded to the server. It
+    | is validated even before files have been processed and hard exception
+    | is thrown.
+    |
+    | Consider setting a reasonable value here, otherwise people may upload GB's
+    | of files which will keep your server busy.
+    |
+    | Also this value is considered when `autoProcess` is set to true.
+    |
+    */
+    maxSize: '20mb',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Auto Process
+    |--------------------------------------------------------------------------
+    |
+    | Whether or not to auto-process files. Since HTTP servers handle files via
+    | couple of specific endpoints. It is better to set this value off and
+    | manually process the files when required.
+    |
+    | This value can contain a boolean or an array of route patterns
+    | to be autoprocessed.
+    */
+    autoProcess: true,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Process Manually
+    |--------------------------------------------------------------------------
+    |
+    | The list of routes that should not process files and instead rely on
+    | manual process. This list should only contain routes when autoProcess
+    | is to true. Otherwise everything is processed manually.
+    |
+    */
+    processManually: []
+
+    /*
+    |--------------------------------------------------------------------------
+    | Temporary file name
+    |--------------------------------------------------------------------------
+    |
+    | Define a function, which should return a string to be used as the
+    | tmp file name.
+    |
+    | If not defined, Bodyparser will use `uuid` as the tmp file name.
+    |
+    | To be defined as. If you are defining the function, then do make sure
+    | to return a value from it.
+    |
+    | tmpFileName () {
+    |   return 'some-unique-value'
+    | }
+    |
+    */
+  }
+}

config/cors.js 87(+87 -0)

diff --git a/config/cors.js b/config/cors.js
new file mode 100644
index 0000000..4c3848e
--- /dev/null
+++ b/config/cors.js
@@ -0,0 +1,87 @@
+'use strict'
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | Origin
+  |--------------------------------------------------------------------------
+  |
+  | Set a list of origins to be allowed. The value can be one of the following
+  |
+  | Boolean: true - Allow current request origin
+  | Boolean: false - Disallow all
+  | String - Comma seperated list of allowed origins
+  | Array - An array of allowed origins
+  | String: * - A wildcard to allow current request origin
+  | Function - Receives the current origin and should return one of the above values.
+  |
+  */
+  origin: false,
+
+  /*
+  |--------------------------------------------------------------------------
+  | Methods
+  |--------------------------------------------------------------------------
+  |
+  | HTTP methods to be allowed. The value can be one of the following
+  |
+  | String - Comma seperated list of allowed methods
+  | Array - An array of allowed methods
+  |
+  */
+  methods: ['GET', 'PUT', 'PATCH', 'POST', 'DELETE'],
+
+  /*
+  |--------------------------------------------------------------------------
+  | Headers
+  |--------------------------------------------------------------------------
+  |
+  | List of headers to be allowed via Access-Control-Request-Headers header.
+  | The value can be on of the following.
+  |
+  | Boolean: true - Allow current request headers
+  | Boolean: false - Disallow all
+  | String - Comma seperated list of allowed headers
+  | Array - An array of allowed headers
+  | String: * - A wildcard to allow current request headers
+  | Function - Receives the current header and should return one of the above values.
+  |
+  */
+  headers: true,
+
+  /*
+  |--------------------------------------------------------------------------
+  | Expose Headers
+  |--------------------------------------------------------------------------
+  |
+  | A list of headers to be exposed via `Access-Control-Expose-Headers`
+  | header. The value can be on of the following.
+  |
+  | Boolean: false - Disallow all
+  | String: Comma seperated list of allowed headers
+  | Array - An array of allowed headers
+  |
+  */
+  exposeHeaders: false,
+
+  /*
+  |--------------------------------------------------------------------------
+  | Credentials
+  |--------------------------------------------------------------------------
+  |
+  | Define Access-Control-Allow-Credentials header. It should always be a
+  | boolean.
+  |
+  */
+  credentials: false,
+
+  /*
+  |--------------------------------------------------------------------------
+  | MaxAge
+  |--------------------------------------------------------------------------
+  |
+  | Define Access-Control-Allow-Max-Age
+  |
+  */
+  maxAge: 90
+}

config/database.js 81(+81 -0)

diff --git a/config/database.js b/config/database.js
new file mode 100644
index 0000000..e9cb916
--- /dev/null
+++ b/config/database.js
@@ -0,0 +1,81 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Env')} */
+const Env = use('Env')
+
+/** @type {import('@adonisjs/ignitor/src/Helpers')} */
+const Helpers = use('Helpers')
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | Default Connection
+  |--------------------------------------------------------------------------
+  |
+  | Connection defines the default connection settings to be used while
+  | interacting with SQL databases.
+  |
+  */
+  connection: Env.get('DB_CONNECTION', 'sqlite'),
+
+  /*
+  |--------------------------------------------------------------------------
+  | Sqlite
+  |--------------------------------------------------------------------------
+  |
+  | Sqlite is a flat file database and can be good choice under development
+  | environment.
+  |
+  | npm i --save sqlite3
+  |
+  */
+  sqlite: {
+    client: 'sqlite3',
+    connection: {
+      filename: Helpers.databasePath(`${Env.get('DB_DATABASE', 'development')}.sqlite`)
+    },
+    useNullAsDefault: true
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | MySQL
+  |--------------------------------------------------------------------------
+  |
+  | Here we define connection settings for MySQL database.
+  |
+  | npm i --save mysql
+  |
+  */
+  mysql: {
+    client: 'mysql',
+    connection: {
+      host: Env.get('DB_HOST', 'localhost'),
+      port: Env.get('DB_PORT', ''),
+      user: Env.get('DB_USER', 'root'),
+      password: Env.get('DB_PASSWORD', ''),
+      database: Env.get('DB_DATABASE', 'adonis')
+    }
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | PostgreSQL
+  |--------------------------------------------------------------------------
+  |
+  | Here we define connection settings for PostgreSQL database.
+  |
+  | npm i --save pg
+  |
+  */
+  pg: {
+    client: 'pg',
+    connection: {
+      host: Env.get('DB_HOST', 'localhost'),
+      port: Env.get('DB_PORT', ''),
+      user: Env.get('DB_USER', 'root'),
+      password: Env.get('DB_PASSWORD', ''),
+      database: Env.get('DB_DATABASE', 'adonis')
+    }
+  }
+}

config/hash.js 49(+49 -0)

diff --git a/config/hash.js b/config/hash.js
new file mode 100644
index 0000000..42f5805
--- /dev/null
+++ b/config/hash.js
@@ -0,0 +1,49 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Env')} */
+const Env = use('Env')
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | Driver
+  |--------------------------------------------------------------------------
+  |
+  | Driver to be used for hashing values. The same driver is used by the
+  | auth module too.
+  |
+  */
+  driver: Env.get('HASH_DRIVER', 'bcrypt'),
+
+  /*
+  |--------------------------------------------------------------------------
+  | Bcrypt
+  |--------------------------------------------------------------------------
+  |
+  | Config related to bcrypt hashing. https://www.npmjs.com/package/bcrypt
+  | package is used internally.
+  |
+  */
+  bcrypt: {
+    rounds: 10
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Argon
+  |--------------------------------------------------------------------------
+  |
+  | Config related to argon. https://www.npmjs.com/package/argon2 package is
+  | used internally.
+  |
+  | Since argon is optional, you will have to install the dependency yourself
+  |
+  |============================================================================
+  | npm i argon2
+  |============================================================================
+  |
+  */
+  argon: {
+    type: 1
+  }
+}

config/session.js 101(+101 -0)

diff --git a/config/session.js b/config/session.js
new file mode 100644
index 0000000..30d6bab
--- /dev/null
+++ b/config/session.js
@@ -0,0 +1,101 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Env')} */
+const Env = use('Env')
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | Session Driver
+  |--------------------------------------------------------------------------
+  |
+  | The session driver to be used for storing session values. It can be
+  | cookie, file or redis.
+  |
+  | For `redis` driver, make sure to install and register `@adonisjs/redis`
+  |
+  */
+  driver: Env.get('SESSION_DRIVER', 'cookie'),
+
+  /*
+  |--------------------------------------------------------------------------
+  | Cookie Name
+  |--------------------------------------------------------------------------
+  |
+  | The name of the cookie to be used for saving session id. Session ids
+  | are signed and encrypted.
+  |
+  */
+  cookieName: 'adonis-session',
+
+  /*
+  |--------------------------------------------------------------------------
+  | Clear session when browser closes
+  |--------------------------------------------------------------------------
+  |
+  | If this value is true, the session cookie will be temporary and will be
+  | removed when browser closes.
+  |
+  */
+  clearWithBrowser: true,
+
+  /*
+  |--------------------------------------------------------------------------
+  | Session age
+  |--------------------------------------------------------------------------
+  |
+  | This value is only used when `clearWithBrowser` is set to false. The
+  | age must be a valid https://npmjs.org/package/ms string or should
+  | be in milliseconds.
+  |
+  | Valid values are:
+  |  '2h', '10d', '5y', '2.5 hrs'
+  |
+  */
+  age: '2h',
+
+  /*
+  |--------------------------------------------------------------------------
+  | Cookie options
+  |--------------------------------------------------------------------------
+  |
+  | Cookie options defines the options to be used for setting up session
+  | cookie
+  |
+  */
+  cookie: {
+    httpOnly: true,
+    sameSite: false,
+    path: '/'
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Sessions location
+  |--------------------------------------------------------------------------
+  |
+  | If driver is set to file, we need to define the relative location from
+  | the temporary path or absolute url to any location.
+  |
+  */
+  file: {
+    location: 'sessions'
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Redis config
+  |--------------------------------------------------------------------------
+  |
+  | The configuration for the redis driver. By default we reference it from
+  | the redis file. But you are free to define an object here too.
+  |
+  */
+  redis: {
+    host: '127.0.0.1',
+    port: 6379,
+    password: null,
+    db: 0,
+    keyPrefix: ''
+  }
+}

config/shield.js 145(+145 -0)

diff --git a/config/shield.js b/config/shield.js
new file mode 100644
index 0000000..255cee3
--- /dev/null
+++ b/config/shield.js
@@ -0,0 +1,145 @@
+'use strict'
+
+module.exports = {
+  /*
+  |--------------------------------------------------------------------------
+  | Content Security Policy
+  |--------------------------------------------------------------------------
+  |
+  | Content security policy filters out the origins not allowed to execute
+  | and load resources like scripts, styles and fonts. There are wide
+  | variety of options to choose from.
+  */
+  csp: {
+    /*
+    |--------------------------------------------------------------------------
+    | Directives
+    |--------------------------------------------------------------------------
+    |
+    | All directives are defined in camelCase and here is the list of
+    | available directives and their possible values.
+    |
+    | https://content-security-policy.com
+    |
+    | @example
+    | directives: {
+    |   defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com']
+    | }
+    |
+    */
+    directives: {
+    },
+    /*
+    |--------------------------------------------------------------------------
+    | Report only
+    |--------------------------------------------------------------------------
+    |
+    | Setting `reportOnly=true` will not block the scripts from running and
+    | instead report them to a URL.
+    |
+    */
+    reportOnly: false,
+    /*
+    |--------------------------------------------------------------------------
+    | Set all headers
+    |--------------------------------------------------------------------------
+    |
+    | Headers staring with `X` have been depreciated, since all major browsers
+    | supports the standard CSP header. So its better to disable deperciated
+    | headers, unless you want them to be set.
+    |
+    */
+    setAllHeaders: false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Disable on android
+    |--------------------------------------------------------------------------
+    |
+    | Certain versions of android are buggy with CSP policy. So you can set
+    | this value to true, to disable it for Android versions with buggy
+    | behavior.
+    |
+    | Here is an issue reported on a different package, but helpful to read
+    | if you want to know the behavior. https://github.com/helmetjs/helmet/pull/82
+    |
+    */
+    disableAndroid: true
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | X-XSS-Protection
+  |--------------------------------------------------------------------------
+  |
+  | X-XSS Protection saves applications from XSS attacks. It is adopted
+  | by IE and later followed by some other browsers.
+  |
+  | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
+  |
+  */
+  xss: {
+    enabled: true,
+    enableOnOldIE: false
+  },
+
+  /*
+  |--------------------------------------------------------------------------
+  | Iframe Options
+  |--------------------------------------------------------------------------
+  |
+  | xframe defines whether or not your website can be embedded inside an
+  | iframe. Choose from one of the following options.
+  | @available options
+  | DENY, SAMEORIGIN, ALLOW-FROM http://example.com
+  |
+  | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+  */
+  xframe: 'DENY',
+
+  /*
+  |--------------------------------------------------------------------------
+  | No Sniff
+  |--------------------------------------------------------------------------
+  |
+  | Browsers have a habit of sniffing content-type of a response. Which means
+  | files with .txt extension containing Javascript code will be executed as
+  | Javascript. You can disable this behavior by setting nosniff to false.
+  |
+  | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+  |
+  */
+  nosniff: true,
+
+  /*
+  |--------------------------------------------------------------------------
+  | No Open
+  |--------------------------------------------------------------------------
+  |
+  | IE users can execute webpages in the context of your website, which is
+  | a serious security risk. Below option will manage this for you.
+  |
+  */
+  noopen: true,
+
+  /*
+  |--------------------------------------------------------------------------
+  | CSRF Protection
+  |--------------------------------------------------------------------------
+  |
+  | CSRF Protection adds another layer of security by making sure, actionable
+  | routes does have a valid token to execute an action.
+  |
+  */
+  csrf: {
+    enable: true,
+    methods: ['POST', 'PUT', 'DELETE'],
+    filterUris: [],
+    cookieOptions: {
+      httpOnly: false,
+      sameSite: true,
+      path: '/',
+      maxAge: 7200
+    }
+  }
+}

database/factory.js 21(+21 -0)

diff --git a/database/factory.js b/database/factory.js
new file mode 100644
index 0000000..16b5084
--- /dev/null
+++ b/database/factory.js
@@ -0,0 +1,21 @@
+'use strict'
+
+/*
+|--------------------------------------------------------------------------
+| Factory
+|--------------------------------------------------------------------------
+|
+| Factories are used to define blueprints for database tables or Lucid
+| models. Later you can use these blueprints to seed your database
+| with dummy data.
+|
+*/
+
+/** @type {import('@adonisjs/lucid/src/Factory')} */
+// const Factory = use('Factory')
+
+// Factory.blueprint('App/Models/User', (faker) => {
+//   return {
+//     username: faker.username()
+//   }
+// })
diff --git a/database/migrations/1503248427885_user.js b/database/migrations/1503248427885_user.js
new file mode 100644
index 0000000..1ade2f5
--- /dev/null
+++ b/database/migrations/1503248427885_user.js
@@ -0,0 +1,22 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class UserSchema extends Schema {
+  up () {
+    this.create('users', (table) => {
+      table.increments()
+      table.string('username', 80).notNullable().unique()
+      table.string('email', 254).notNullable().unique()
+      table.string('password', 60).notNullable()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('users')
+  }
+}
+
+module.exports = UserSchema
diff --git a/database/migrations/1503248427886_token.js b/database/migrations/1503248427886_token.js
new file mode 100644
index 0000000..c8bb9fc
--- /dev/null
+++ b/database/migrations/1503248427886_token.js
@@ -0,0 +1,23 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class TokensSchema extends Schema {
+  up () {
+    this.create('tokens', (table) => {
+      table.increments()
+      table.integer('user_id').unsigned().references('id').inTable('users')
+      table.string('token', 255).notNullable().unique().index()
+      table.string('type', 80).notNullable()
+      table.boolean('is_revoked').defaultTo(false)
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('tokens')
+  }
+}
+
+module.exports = TokensSchema
diff --git a/database/migrations/1575321261885_themes_schema.js b/database/migrations/1575321261885_themes_schema.js
new file mode 100644
index 0000000..bdc0da5
--- /dev/null
+++ b/database/migrations/1575321261885_themes_schema.js
@@ -0,0 +1,19 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class ThemesSchema extends Schema {
+  up () {
+    this.create('themes', (table) => {
+      table.increments('id')
+      table.string('name').notNullable().unique()
+    })
+  }
+
+  down () {
+    this.drop('themes')
+  }
+}
+
+module.exports = ThemesSchema
diff --git a/database/migrations/1575321574938_elements_schema.js b/database/migrations/1575321574938_elements_schema.js
new file mode 100644
index 0000000..a7f54f7
--- /dev/null
+++ b/database/migrations/1575321574938_elements_schema.js
@@ -0,0 +1,22 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class ElementsSchema extends Schema {
+  up () {
+    this.create('elements', (table) => {
+      table.increments('id')
+      table.string('name').notNullable()
+      table.integer('theme_id').notNullable().unsigned().references('id').inTable('themes')
+      table.integer('priority').nullable().unsigned()
+      table.integer('refresh_rate').nullable()
+    })
+  }
+
+  down () {
+    this.drop('elements')
+  }
+}
+
+module.exports = ElementsSchema
diff --git a/database/migrations/1575321954825_element_notes_schema.js b/database/migrations/1575321954825_element_notes_schema.js
new file mode 100644
index 0000000..6a4ecfc
--- /dev/null
+++ b/database/migrations/1575321954825_element_notes_schema.js
@@ -0,0 +1,21 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class ElementNotesSchema extends Schema {
+  up () {
+    this.create('element_notes', (table) => {
+      table.increments('id')
+      table.integer('element_id').notNullable().unsigned().references('id').inTable('elements')
+      table.text('content').notNullable()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('element_notes')
+  }
+}
+
+module.exports = ElementNotesSchema
diff --git a/database/migrations/1575322085612_standards_schema.js b/database/migrations/1575322085612_standards_schema.js
new file mode 100644
index 0000000..31b756a
--- /dev/null
+++ b/database/migrations/1575322085612_standards_schema.js
@@ -0,0 +1,23 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class StandardsSchema extends Schema {
+  up () {
+    this.create('standards', (table) => {
+      table.increments('id')
+      table.integer('element_id').notNullable().unsigned().references('id').inTable('elements')
+      table.string('document').notNullable()
+      table.float('version', 2).unsigned().notNullable()
+      table.timestamps()
+      table.date('retired').nullable()
+    })
+  }
+
+  down () {
+    this.drop('standards')
+  }
+}
+
+module.exports = StandardsSchema
diff --git a/database/migrations/1575322303589_standards_notes_schema.js b/database/migrations/1575322303589_standards_notes_schema.js
new file mode 100644
index 0000000..f0ed7b7
--- /dev/null
+++ b/database/migrations/1575322303589_standards_notes_schema.js
@@ -0,0 +1,21 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class StandardsNotesSchema extends Schema {
+  up () {
+    this.create('standards_notes', (table) => {
+      table.increments('id')
+      table.integer('standard_id').notNullable().unsigned().references('id').inTable('standards')
+      table.text('content').notNullable()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('standards_notes')
+  }
+}
+
+module.exports = StandardsNotesSchema
diff --git a/database/migrations/1575322481931_organization_types_schema.js b/database/migrations/1575322481931_organization_types_schema.js
new file mode 100644
index 0000000..ac46e44
--- /dev/null
+++ b/database/migrations/1575322481931_organization_types_schema.js
@@ -0,0 +1,19 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class OrganizationTypesSchema extends Schema {
+  up () {
+    this.create('organization_types', (table) => {
+      table.increments('id')
+      table.string('type').notNullable()
+    })
+  }
+
+  down () {
+    this.drop('organization_types')
+  }
+}
+
+module.exports = OrganizationTypesSchema
diff --git a/database/migrations/1575322618141_organizations_schema.js b/database/migrations/1575322618141_organizations_schema.js
new file mode 100644
index 0000000..51fe867
--- /dev/null
+++ b/database/migrations/1575322618141_organizations_schema.js
@@ -0,0 +1,23 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class OrganizationsSchema extends Schema {
+  up () {
+    this.create('organizations', (table) => {
+      table.increments('id')
+      table.string('name').notNullable().unique()
+      table.string('email').notNullable()
+      table.string('phone').notNullable()
+      table.integer('organization_type_id').notNullable().references('id').inTable('organization_types')
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('organizations')
+  }
+}
+
+module.exports = OrganizationsSchema
diff --git a/database/migrations/1575322806187_contacts_schema.js b/database/migrations/1575322806187_contacts_schema.js
new file mode 100644
index 0000000..06b7ca8
--- /dev/null
+++ b/database/migrations/1575322806187_contacts_schema.js
@@ -0,0 +1,24 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class ContactsSchema extends Schema {
+  up () {
+    this.create('contacts', (table) => {
+      table.increments('id')
+      table.string('email').notNullable().unique()
+      table.string('first_name').notNullable()
+      table.string('last_name').notNullable()
+      table.string('phone').notNullable()
+      table.integer('organization_id').notNullable().references('id').inTable('organizations')
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('contacts')
+  }
+}
+
+module.exports = ContactsSchema
diff --git a/database/migrations/1575322970641_contact_notes_schema.js b/database/migrations/1575322970641_contact_notes_schema.js
new file mode 100644
index 0000000..fdec2d8
--- /dev/null
+++ b/database/migrations/1575322970641_contact_notes_schema.js
@@ -0,0 +1,21 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class ContactNotesSchema extends Schema {
+  up () {
+    this.create('contact_notes', (table) => {
+      table.increments('id')
+      table.integer('contact_id').notNullable().unsigned().references('id').inTable('contacts')
+      table.text('content').notNullable()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('contact_notes')
+  }
+}
+
+module.exports = ContactNotesSchema
diff --git a/database/migrations/1575323184990_funding_schema.js b/database/migrations/1575323184990_funding_schema.js
new file mode 100644
index 0000000..583dd4d
--- /dev/null
+++ b/database/migrations/1575323184990_funding_schema.js
@@ -0,0 +1,23 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class FundingSchema extends Schema {
+  up () {
+    this.create('fundings', (table) => {
+      table.increments('id')
+      table.float('amount', 2).notNullable()
+      table.string('document').notNullable()
+      table.date('date_awarded').notNullable()
+      table.integer('element_id').notNullable().references('id').inTable('elements')
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('fundings')
+  }
+}
+
+module.exports = FundingSchema
diff --git a/database/migrations/1575323309230_stewards_schema.js b/database/migrations/1575323309230_stewards_schema.js
new file mode 100644
index 0000000..6ec71a6
--- /dev/null
+++ b/database/migrations/1575323309230_stewards_schema.js
@@ -0,0 +1,22 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class StewardsSchema extends Schema {
+  up () {
+    this.create('stewards', (table) => {
+      table.increments('id')
+      table.integer('contact_id').notNullable().references('id').inTable('contacts')
+      table.date('start_date').notNullable()
+      table.date('end_date').notNullable()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('stewards')
+  }
+}
+
+module.exports = StewardsSchema
diff --git a/database/migrations/1575323434337_elements_stewards_schema.js b/database/migrations/1575323434337_elements_stewards_schema.js
new file mode 100644
index 0000000..1077dd7
--- /dev/null
+++ b/database/migrations/1575323434337_elements_stewards_schema.js
@@ -0,0 +1,19 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class ElementsStewardsSchema extends Schema {
+  up () {
+    this.create('elements_stewards', (table) => {
+      table.integer('element_id').references('id').inTable('elements')
+      table.integer('steward_id').references('id').inTable('stewards')
+    })
+  }
+
+  down () {
+    this.drop('elements_stewards')
+  }
+}
+
+module.exports = ElementsStewardsSchema
diff --git a/database/migrations/1575323518345_submission_status_schema.js b/database/migrations/1575323518345_submission_status_schema.js
new file mode 100644
index 0000000..c12e740
--- /dev/null
+++ b/database/migrations/1575323518345_submission_status_schema.js
@@ -0,0 +1,20 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class SubmissionStatusSchema extends Schema {
+  up () {
+    this.create('submission_statuses', (table) => {
+      table.increments()
+      table.string('status').notNullable().unique()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('submission_statuses')
+  }
+}
+
+module.exports = SubmissionStatusSchema
diff --git a/database/migrations/1575323565424_submissions_schema.js b/database/migrations/1575323565424_submissions_schema.js
new file mode 100644
index 0000000..7f53eb6
--- /dev/null
+++ b/database/migrations/1575323565424_submissions_schema.js
@@ -0,0 +1,33 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class SubmissionsSchema extends Schema {
+  up () {
+    this.create('submissions', (table) => {
+      table.increments('id')
+      table.string('name')
+      table.integer('element_id').notNullable().references('id').inTable('elements')
+      table.integer('steward_id').notNullable().references('id').inTable('stewards')
+      table.date('upload_date').notNullable()
+      table.date('creation_date').notNullable()
+      table.date('published_date').notNullable()
+      table.json('metadata').nullable()
+      table.string('local_file_location').nullable()
+      table.string('download_file_location').nullable()
+      table.string('service_location_ags').nullable()
+      table.string('service_location_agol').nullable()
+      table.integer('standard_id').references('id').inTable('standards').nullable()
+      table.boolean('public').notNullable()
+      table.integer('status_id').notNullable().references('id').inTable('submission_statuses')
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('submissions')
+  }
+}
+
+module.exports = SubmissionsSchema
diff --git a/database/migrations/1575323907842_submission_notes_schema.js b/database/migrations/1575323907842_submission_notes_schema.js
new file mode 100644
index 0000000..03c13ee
--- /dev/null
+++ b/database/migrations/1575323907842_submission_notes_schema.js
@@ -0,0 +1,21 @@
+'use strict'
+
+/** @type {import('@adonisjs/lucid/src/Schema')} */
+const Schema = use('Schema')
+
+class SubmissionNotesSchema extends Schema {
+  up () {
+    this.create('submission_notes', (table) => {
+      table.increments('id')
+      table.integer('submission_id').notNullable().unsigned().references('id').inTable('submissions')
+      table.text('content').notNullable()
+      table.timestamps()
+    })
+  }
+
+  down () {
+    this.drop('submission_notes')
+  }
+}
+
+module.exports = SubmissionNotesSchema

package.json 35(+35 -0)

diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d4743c3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+  "name": "adonis-fullstack-app",
+  "version": "4.1.0",
+  "adonis-version": "4.1.0",
+  "description": "The fullstack application boilerplate for Adonisjs",
+  "main": "index.js",
+  "scripts": {
+    "start": "node server.js",
+    "test": "node ace test"
+  },
+  "keywords": [
+    "adonisjs",
+    "adonis-app"
+  ],
+  "author": "",
+  "license": "UNLICENSED",
+  "private": true,
+  "dependencies": {
+    "@adonisjs/ace": "^5.0.8",
+    "@adonisjs/auth": "^3.0.7",
+    "@adonisjs/bodyparser": "^2.0.5",
+    "@adonisjs/cors": "^1.0.7",
+    "@adonisjs/fold": "^4.0.9",
+    "@adonisjs/framework": "^5.0.9",
+    "@adonisjs/ignitor": "^2.0.8",
+    "@adonisjs/lucid": "^6.1.3",
+    "@adonisjs/session": "^1.0.27",
+    "@adonisjs/shield": "^1.0.8",
+    "pg": "^7.14.0"
+  },
+  "devDependencies": {},
+  "autoload": {
+    "App": "./app"
+  }
+}
diff --git a/public/css/main.css b/public/css/main.css
new file mode 100644
index 0000000..4328a91
--- /dev/null
+++ b/public/css/main.css
@@ -0,0 +1 @@
+/* main.css */
\ No newline at end of file

public/logo.svg 1(+1 -0)

diff --git a/public/logo.svg b/public/logo.svg
new file mode 100644
index 0000000..c8be274
--- /dev/null
+++ b/public/logo.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg width="36px" height="33px" viewBox="0 0 36 33" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(0 .5)" fill="none" fill-rule="evenodd"><path d="M20 2.236L5.618 31h28.764L20 2.236z" stroke="#FFFFFF" stroke-width="2"/><path fill="#FFFFFF" d="M12 2l12 24H0"/></g></svg>
diff --git a/public/pyramid.png b/public/pyramid.png
new file mode 100644
index 0000000..369ab18
Binary files /dev/null and b/public/pyramid.png differ
diff --git a/public/splash.png b/public/splash.png
new file mode 100644
index 0000000..7ea4fe0
Binary files /dev/null and b/public/splash.png differ

public/style.css 92(+92 -0)

diff --git a/public/style.css b/public/style.css
new file mode 100644
index 0000000..c805815
--- /dev/null
+++ b/public/style.css
@@ -0,0 +1,92 @@
+@import url('https://fonts.googleapis.com/css?family=Montserrat:300');
+
+html, body {
+  height: 100%;
+  width: 100%;
+}
+
+body {
+  font-family: 'Montserrat', sans-serif;
+  font-weight: 300;
+  background-image: url("/splash.png");
+  background-color: #220052;
+}
+
+* {
+  margin: 0;
+  padding: 0;
+}
+
+section {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  max-width: 536px;
+  margin: auto;
+  position: relative;
+}
+
+section:before {
+  content: "";
+  position: absolute;
+  background: url("/pyramid.png") no-repeat;
+  background-size: 100%;
+  width: 100%;
+  height: 402px;
+  z-index: -1;
+}
+
+.logo {
+  background: url("/logo.svg") no-repeat;
+  width: 36px;
+  height: 33px;
+  background-size: 100%;
+  margin-bottom: 35px;
+  opacity: 0;
+  animation: slideUp 1s cubic-bezier(0.19, 1, 0.30, 1) 1.3s forwards;
+}
+
+.title {
+  background: url("/title.svg") no-repeat;
+  width: 219px;
+  height: 36px;
+  background-size: 100%;
+  opacity: 0;
+  animation: slideUp 1s cubic-bezier(0.19, 1, 0.30, 1) 0.2s forwards;
+}
+
+.subtitle {
+  margin-top: 25px;
+  color: #BDB3CB;
+  font-size: 17px;
+  text-align: center;
+  letter-spacing: 0.5;
+  opacity: 0;
+  animation: slideUp 1s cubic-bezier(0.19, 1, 0.30, 1) 0.5s forwards;
+}
+
+
+a {
+  color: inherit;
+  text-decoration: underline;
+}
+
+p {
+  margin: 0.83rem 0;
+}
+
+@keyframes slideUp {
+  0% {
+    transform: translateY(40px);
+    opacity: 0;
+  }
+  50% {
+    opacity: 0.2%;
+  }
+  100% {
+    opacity: 1;
+    transform: none;
+  }
+}

public/title.svg 1(+1 -0)

diff --git a/public/title.svg b/public/title.svg
new file mode 100644
index 0000000..38dea69
--- /dev/null
+++ b/public/title.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg width="186px" height="31px" viewBox="0 0 186 31" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M20.25 6.106V.1H.72v6.006h5.712v17.388H.72V29.5h19.53v-6.006h-5.712V6.106h5.712zm18.774 7.35v-5.46h-6.846V1.528h-7.182v3.108c0 2.226-1.218 3.36-3.444 3.36h-.084v5.46h3.528v8.904c0 4.578 3.528 7.686 8.82 7.686 1.932 0 3.276-.126 5.208-1.05v-5.964c-2.016.882-2.982.966-3.822.966-1.806 0-3.024-1.008-3.024-2.898v-7.644h6.846zm37.17-5.46l-3.612 13.692L68.76 7.996H63.3l-3.822 13.692-3.654-13.692h-7.308L55.068 29.5h7.266l3.696-12.516L69.684 29.5h7.308l6.552-21.504h-7.35zM95.43 7.45c6.846 0 11.424 4.746 11.424 11.256 0 6.552-4.578 11.34-11.424 11.34-6.888 0-11.466-4.788-11.466-11.34 0-6.51 4.578-11.256 11.466-11.256zm0 5.628c-2.898 0-4.62 2.352-4.62 5.628 0 3.318 1.722 5.712 4.62 5.712 2.856 0 4.578-2.394 4.578-5.712 0-3.276-1.722-5.628-4.578-5.628zm22.092.714V7.996h-7.182V29.5h7.182v-7.518c0-5.376 1.89-7.728 8.946-6.972V7.534c-4.788 0-7.686 1.806-8.946 6.258zM145.158 29.5h8.4l-7.812-11.886 7.14-9.618h-8.316l-4.284 6.552h-3.612V.1h-7.182v29.4h7.182v-8.442h3.612l4.872 8.442zm22.092-14.196h6.384c-.462-5.46-4.326-7.854-9.87-7.854-6.09 0-9.534 2.436-9.534 6.804 0 4.998 4.494 5.586 8.19 6.426 3.822.882 4.704 1.134 4.704 2.478 0 1.512-1.428 1.932-2.982 1.932-2.394 0-3.822-.966-4.074-3.486h-6.384c.336 5.88 4.536 8.442 10.542 8.442 6.132 0 9.66-2.688 9.66-7.14 0-4.998-4.41-5.628-8.736-6.594-3.234-.672-4.326-.882-4.326-2.31 0-1.134 1.176-1.848 2.856-1.848 2.268 0 3.276.882 3.57 3.15zm11.424 4.536h6.258L185.94.1h-8.316l1.05 19.74zm-.63 9.66h7.518v-6.51h-7.518v6.51z" fill="#FFFFFF" fill-rule="evenodd"/></svg>

README.md 31(+31 -0)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d78d10e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Adonis fullstack application
+
+This is the fullstack boilerplate for AdonisJs, it comes pre-configured with.
+
+1. Bodyparser
+2. Session
+3. Authentication
+4. Web security middleware
+5. CORS
+6. Edge template engine
+7. Lucid ORM
+8. Migrations and seeds
+
+## Setup
+
+Use the adonis command to install the blueprint
+
+```bash
+adonis new yardstick
+```
+
+or manually clone the repo and then run `npm install`.
+
+
+### Migrations
+
+Run the following command to run startup migrations.
+
+```js
+adonis migration:run
+```
diff --git a/resources/views/inc/footer.edge b/resources/views/inc/footer.edge
new file mode 100644
index 0000000..2337a3d
--- /dev/null
+++ b/resources/views/inc/footer.edge
@@ -0,0 +1 @@
+<footer class="footer has-text-centered">Oregon Geospatial Enterprise Office</footer>
\ No newline at end of file
diff --git a/resources/views/inc/nav.edge b/resources/views/inc/nav.edge
new file mode 100644
index 0000000..2737bf1
--- /dev/null
+++ b/resources/views/inc/nav.edge
@@ -0,0 +1,6 @@
+<section class="box">
+  <h1 class="is-size-4">Data Inventory Tracking</h1>
+</section>
+<ul>
+  <li>Navigation Link</li>
+</ul>
\ No newline at end of file
diff --git a/resources/views/index.edge b/resources/views/index.edge
new file mode 100644
index 0000000..62ac5e1
--- /dev/null
+++ b/resources/views/index.edge
@@ -0,0 +1,7 @@
+@layout('layouts.main')
+
+@section('content')
+
+Welcome to the Oregon Geospatial Enterprise office data inventory tracking page.
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/layouts/main.edge b/resources/views/layouts/main.edge
new file mode 100644
index 0000000..9d391bd
--- /dev/null
+++ b/resources/views/layouts/main.edge
@@ -0,0 +1,31 @@
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <meta name="description" content="ArcGIS Online Configuration Backups for Versioning and Recovery">
+    <meta name="keywords" content="arcgis, backup, arcgis online, esri, recovery, versioning, tool, agol">
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css" integrity="sha256-vK3UTo/8wHbaUn+dTQD0X6dzidqc5l7gczvH+Bnowwk=" crossorigin="anonymous" />
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/cash/4.1.2/cash.min.js"></script>
+    <title>navigatOR Data Inventory Tracking</title>
+    {{ style('css/main.css') }}
+    @!section('head')
+</head>
+<body class='container'>
+  <div class="content">
+    <div class="columns">
+      <div class="column is-3">
+        <!-- Navigation Left Column -->
+        @include('inc.nav')
+      </div>
+      <div class="column is-9">
+        <!-- Main Right Column -->
+        @!section('content')
+      </div>
+    </div>
+    @include('inc.footer')
+  </div>
+@!section('scripts')
+{{ script('js/main.js') }}
+</body>
+</html>
\ No newline at end of file
diff --git a/resources/views/welcome.edge b/resources/views/welcome.edge
new file mode 100644
index 0000000..4355627
--- /dev/null
+++ b/resources/views/welcome.edge
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>Hello Adonis</title>
+  {{ style('style') }}
+</head>
+<body>
+  <section>
+    <div class="logo"></div>
+    <div class="title"></div>
+    <div class="subtitle">
+      <p>AdonisJs simplicity will make you feel confident about your code</p>
+      <p>
+        Don't know where to start? Read the <a href="https://adonisjs.com/docs">documentation</a>.
+      </p>
+    </div>
+  </section>
+</body>
+</html>

server.js 25(+25 -0)

diff --git a/server.js b/server.js
new file mode 100644
index 0000000..c3a987b
--- /dev/null
+++ b/server.js
@@ -0,0 +1,25 @@
+'use strict'
+
+/*
+|--------------------------------------------------------------------------
+| Http server
+|--------------------------------------------------------------------------
+|
+| This file bootstrap Adonisjs to start the HTTP server. You are free to
+| customize the process of booting the http server.
+|
+| """ Loading ace commands """
+|     At times you may want to load ace commands when starting the HTTP server.
+|     Same can be done by chaining `loadCommands()` method after
+|
+| """ Preloading files """
+|     Also you can preload files by calling `preLoad('path/to/file')` method.
+|     Make sure to pass relative path from the project root.
+*/
+
+const { Ignitor } = require('@adonisjs/ignitor')
+
+new Ignitor(require('@adonisjs/fold'))
+  .appRoot(__dirname)
+  .fireHttpServer()
+  .catch(console.error)

start/app.js 61(+61 -0)

diff --git a/start/app.js b/start/app.js
new file mode 100644
index 0000000..e42f331
--- /dev/null
+++ b/start/app.js
@@ -0,0 +1,61 @@
+'use strict'
+
+/*
+|--------------------------------------------------------------------------
+| Providers
+|--------------------------------------------------------------------------
+|
+| Providers are building blocks for your Adonis app. Anytime you install
+| a new Adonis specific package, chances are you will register the
+| provider here.
+|
+*/
+const providers = [
+  '@adonisjs/framework/providers/AppProvider',
+  '@adonisjs/framework/providers/ViewProvider',
+  '@adonisjs/lucid/providers/LucidProvider',
+  '@adonisjs/bodyparser/providers/BodyParserProvider',
+  '@adonisjs/cors/providers/CorsProvider',
+  '@adonisjs/shield/providers/ShieldProvider',
+  '@adonisjs/session/providers/SessionProvider',
+  '@adonisjs/auth/providers/AuthProvider'
+]
+
+/*
+|--------------------------------------------------------------------------
+| Ace Providers
+|--------------------------------------------------------------------------
+|
+| Ace providers are required only when running ace commands. For example
+| Providers for migrations, tests etc.
+|
+*/
+const aceProviders = [
+  '@adonisjs/lucid/providers/MigrationsProvider'
+]
+
+/*
+|--------------------------------------------------------------------------
+| Aliases
+|--------------------------------------------------------------------------
+|
+| Aliases are short unique names for IoC container bindings. You are free
+| to create your own aliases.
+|
+| For example:
+|   { Route: 'Adonis/Src/Route' }
+|
+*/
+const aliases = {}
+
+/*
+|--------------------------------------------------------------------------
+| Commands
+|--------------------------------------------------------------------------
+|
+| Here you store ace commands for your package
+|
+*/
+const commands = []
+
+module.exports = { providers, aceProviders, aliases, commands }

start/kernel.js 63(+63 -0)

diff --git a/start/kernel.js b/start/kernel.js
new file mode 100644
index 0000000..b4f2720
--- /dev/null
+++ b/start/kernel.js
@@ -0,0 +1,63 @@
+'use strict'
+
+/** @type {import('@adonisjs/framework/src/Server')} */
+const Server = use('Server')
+
+/*
+|--------------------------------------------------------------------------
+| Global Middleware
+|--------------------------------------------------------------------------
+|
+| Global middleware are executed on each http request only when the routes
+| match.
+|
+*/
+const globalMiddleware = [
+  'Adonis/Middleware/BodyParser',
+  'Adonis/Middleware/Session',
+  'Adonis/Middleware/Shield',
+  'Adonis/Middleware/AuthInit',
+  'App/Middleware/ConvertEmptyStringsToNull',
+]
+
+/*
+|--------------------------------------------------------------------------
+| Named Middleware
+|--------------------------------------------------------------------------
+|
+| Named middleware is key/value object to conditionally add middleware on
+| specific routes or group of routes.
+|
+| // define
+| {
+|   auth: 'Adonis/Middleware/Auth'
+| }
+|
+| // use
+| Route.get().middleware('auth')
+|
+*/
+const namedMiddleware = {
+  auth: 'Adonis/Middleware/Auth',
+  guest: 'Adonis/Middleware/AllowGuestOnly'
+}
+
+/*
+|--------------------------------------------------------------------------
+| Server Middleware
+|--------------------------------------------------------------------------
+|
+| Server level middleware are executed even when route for a given URL is
+| not registered. Features like `static assets` and `cors` needs better
+| control over request lifecycle.
+|
+*/
+const serverMiddleware = [
+  'Adonis/Middleware/Static',
+  'Adonis/Middleware/Cors'
+]
+
+Server
+  .registerGlobal(globalMiddleware)
+  .registerNamed(namedMiddleware)
+  .use(serverMiddleware)

start/routes.js 20(+20 -0)

diff --git a/start/routes.js b/start/routes.js
new file mode 100644
index 0000000..c6ad938
--- /dev/null
+++ b/start/routes.js
@@ -0,0 +1,20 @@
+'use strict'
+
+/*
+|--------------------------------------------------------------------------
+| Routes
+|--------------------------------------------------------------------------
+|
+| Http routes are entry points to your web application. You can create
+| routes for different URL's and bind Controller actions to them.
+|
+| A complete guide on routing is available here.
+| http://adonisjs.com/docs/4.1/routing
+|
+*/
+
+/** @type {typeof import('@adonisjs/framework/src/Route/Manager')} */
+const Route = use('Route')
+
+// Main routes
+Route.on('/').render('index')
\ No newline at end of file