├── .editorconfig ├── .env.example ├── .gitignore ├── README.md ├── ace ├── app ├── Middleware │ └── ConvertEmptyStringsToNull.js └── Models │ ├── Token.js │ ├── Traits │ └── NoTimestamp.js │ └── User.js ├── config ├── app.js ├── auth.js ├── bodyParser.js ├── cors.js ├── database.js ├── hash.js ├── session.js └── shield.js ├── database ├── factory.js └── migrations │ ├── 1503248427885_user.js │ └── 1503248427886_token.js ├── package.json ├── public ├── logo.svg ├── pyramid.png ├── splash.png ├── style.css └── title.svg ├── resources └── views │ └── welcome.edge ├── server.js └── start ├── app.js ├── kernel.js └── routes.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | HOST=127.0.0.1 2 | PORT=3333 3 | NODE_ENV=development 4 | APP_URL=http://${HOST}:${PORT} 5 | 6 | CACHE_VIEWS=false 7 | 8 | APP_KEY= 9 | 10 | DB_CONNECTION=sqlite 11 | DB_HOST=127.0.0.1 12 | DB_PORT=3306 13 | DB_USER=root 14 | DB_PASSWORD= 15 | DB_DATABASE=adonis 16 | 17 | SESSION_DRIVER=cookie 18 | HASH_DRIVER=bcrypt 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules 2 | node_modules 3 | package-lock.json 4 | 5 | # Adonis directory for storing tmp files 6 | tmp 7 | 8 | # Environment variables, never commit this file 9 | .env 10 | 11 | # The development sqlite file 12 | database/*.sqlite 13 | 14 | # VSCode & Webstorm history directories 15 | .history 16 | .idea 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adonis fullstack application 2 | 3 | This is the fullstack boilerplate for AdonisJs, it comes pre-configured with. 4 | 5 | 1. Bodyparser 6 | 2. Session 7 | 3. Authentication 8 | 4. Web security middleware 9 | 5. CORS 10 | 6. Edge template engine 11 | 7. Lucid ORM 12 | 8. Migrations and seeds 13 | 14 | ## Setup 15 | 16 | Use the adonis command to install the blueprint 17 | 18 | ```bash 19 | adonis new yardstick 20 | ``` 21 | 22 | or manually clone the repo and then run `npm install`. 23 | 24 | 25 | ### Migrations 26 | 27 | Run the following command to run startup migrations. 28 | 29 | ```js 30 | adonis migration:run 31 | ``` 32 | -------------------------------------------------------------------------------- /ace: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Ace Commands 6 | |-------------------------------------------------------------------------- 7 | | 8 | | The ace file is just a regular Javascript file but with no extension. You 9 | | can call `node ace` followed by the command name and it just works. 10 | | 11 | | Also you can use `adonis` followed by the command name, since the adonis 12 | | global proxy all the ace commands. 13 | | 14 | */ 15 | 16 | const { Ignitor } = require('@adonisjs/ignitor') 17 | 18 | new Ignitor(require('@adonisjs/fold')) 19 | .appRoot(__dirname) 20 | .fireAce() 21 | .catch(console.error) 22 | -------------------------------------------------------------------------------- /app/Middleware/ConvertEmptyStringsToNull.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class ConvertEmptyStringsToNull { 4 | async handle ({ request }, next) { 5 | if (Object.keys(request.body).length) { 6 | request.body = Object.assign( 7 | ...Object.keys(request.body).map(key => ({ 8 | [key]: request.body[key] !== '' ? request.body[key] : null 9 | })) 10 | ) 11 | } 12 | 13 | await next() 14 | } 15 | } 16 | 17 | module.exports = ConvertEmptyStringsToNull 18 | -------------------------------------------------------------------------------- /app/Models/Token.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */ 4 | const Model = use('Model') 5 | 6 | class Token extends Model { 7 | } 8 | 9 | module.exports = Token 10 | -------------------------------------------------------------------------------- /app/Models/Traits/NoTimestamp.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class NoTimestamp { 4 | register (Model) { 5 | Object.defineProperties(Model, { 6 | createdAtColumn: { 7 | get: () => null, 8 | }, 9 | updatedAtColumn: { 10 | get: () => null, 11 | }, 12 | }) 13 | } 14 | } 15 | 16 | module.exports = NoTimestamp 17 | -------------------------------------------------------------------------------- /app/Models/User.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/framework/src/Hash')} */ 4 | const Hash = use('Hash') 5 | 6 | /** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */ 7 | const Model = use('Model') 8 | 9 | class User extends Model { 10 | static boot () { 11 | super.boot() 12 | 13 | /** 14 | * A hook to hash the user password before saving 15 | * it to the database. 16 | */ 17 | this.addHook('beforeSave', async (userInstance) => { 18 | if (userInstance.dirty.password) { 19 | userInstance.password = await Hash.make(userInstance.password) 20 | } 21 | }) 22 | } 23 | 24 | /** 25 | * A relationship on tokens is required for auth to 26 | * work. Since features like `refreshTokens` or 27 | * `rememberToken` will be saved inside the 28 | * tokens table. 29 | * 30 | * @method tokens 31 | * 32 | * @return {Object} 33 | */ 34 | tokens () { 35 | return this.hasMany('App/Models/Token') 36 | } 37 | } 38 | 39 | module.exports = User 40 | -------------------------------------------------------------------------------- /config/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/framework/src/Env')} */ 4 | const Env = use('Env') 5 | 6 | module.exports = { 7 | 8 | /* 9 | |-------------------------------------------------------------------------- 10 | | Application Name 11 | |-------------------------------------------------------------------------- 12 | | 13 | | This value is the name of your application and can be used when you 14 | | need to place the application's name in a email, view or 15 | | other location. 16 | | 17 | */ 18 | 19 | name: Env.get('APP_NAME', 'AdonisJs'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | App Key 24 | |-------------------------------------------------------------------------- 25 | | 26 | | App key is a randomly generated 16 or 32 characters long string required 27 | | to encrypted cookies, sessions and other sensitive data. 28 | | 29 | */ 30 | appKey: Env.getOrFail('APP_KEY'), 31 | 32 | http: { 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Allow Method Spoofing 36 | |-------------------------------------------------------------------------- 37 | | 38 | | Method spoofing allows you to make requests by spoofing the http verb. 39 | | Which means you can make a GET request but instruct the server to 40 | | treat as a POST or PUT request. If you want this feature, set the 41 | | below value to true. 42 | | 43 | */ 44 | allowMethodSpoofing: true, 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Trust Proxy 49 | |-------------------------------------------------------------------------- 50 | | 51 | | Trust proxy defines whether X-Forwarded-* headers should be trusted or not. 52 | | When your application is behind a proxy server like nginx, these values 53 | | are set automatically and should be trusted. Apart from setting it 54 | | to true or false Adonis supports a handful of ways to allow proxy 55 | | values. Read documentation for that. 56 | | 57 | */ 58 | trustProxy: false, 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Subdomains 63 | |-------------------------------------------------------------------------- 64 | | 65 | | Offset to be used for returning subdomains for a given request. For 66 | | majority of applications it will be 2, until you have nested 67 | | sudomains. 68 | | cheatsheet.adonisjs.com - offset - 2 69 | | virk.cheatsheet.adonisjs.com - offset - 3 70 | | 71 | */ 72 | subdomainOffset: 2, 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | JSONP Callback 77 | |-------------------------------------------------------------------------- 78 | | 79 | | Default jsonp callback to be used when callback query string is missing 80 | | in request url. 81 | | 82 | */ 83 | jsonpCallback: 'callback', 84 | 85 | 86 | /* 87 | |-------------------------------------------------------------------------- 88 | | Etag 89 | |-------------------------------------------------------------------------- 90 | | 91 | | Set etag on all HTTP responses. In order to disable for selected routes, 92 | | you can call the `response.send` with an options object as follows. 93 | | 94 | | response.send('Hello', { ignoreEtag: true }) 95 | | 96 | */ 97 | etag: false 98 | }, 99 | 100 | views: { 101 | /* 102 | |-------------------------------------------------------------------------- 103 | | Cache Views 104 | |-------------------------------------------------------------------------- 105 | | 106 | | Define whether or not to cache the compiled view. Set it to true in 107 | | production to optimize view loading time. 108 | | 109 | */ 110 | cache: Env.get('CACHE_VIEWS', true) 111 | }, 112 | 113 | static: { 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Dot Files 117 | |-------------------------------------------------------------------------- 118 | | 119 | | Define how to treat dot files when trying to serve static resources. 120 | | By default it is set to ignore, which will pretend that dotfiles 121 | | do not exist. 122 | | 123 | | Can be one of the following 124 | | ignore, deny, allow 125 | | 126 | */ 127 | dotfiles: 'ignore', 128 | 129 | /* 130 | |-------------------------------------------------------------------------- 131 | | ETag 132 | |-------------------------------------------------------------------------- 133 | | 134 | | Enable or disable etag generation 135 | | 136 | */ 137 | etag: true, 138 | 139 | /* 140 | |-------------------------------------------------------------------------- 141 | | Extensions 142 | |-------------------------------------------------------------------------- 143 | | 144 | | Set file extension fallbacks. When set, if a file is not found, the given 145 | | extensions will be added to the file name and search for. The first 146 | | that exists will be served. Example: ['html', 'htm']. 147 | | 148 | */ 149 | extensions: false 150 | }, 151 | 152 | locales: { 153 | /* 154 | |-------------------------------------------------------------------------- 155 | | Loader 156 | |-------------------------------------------------------------------------- 157 | | 158 | | The loader to be used for fetching and updating locales. Below is the 159 | | list of available options. 160 | | 161 | | file, database 162 | | 163 | */ 164 | loader: 'file', 165 | 166 | /* 167 | |-------------------------------------------------------------------------- 168 | | Default Locale 169 | |-------------------------------------------------------------------------- 170 | | 171 | | Default locale to be used by Antl provider. You can always switch drivers 172 | | in runtime or use the official Antl middleware to detect the driver 173 | | based on HTTP headers/query string. 174 | | 175 | */ 176 | locale: 'en' 177 | }, 178 | 179 | logger: { 180 | /* 181 | |-------------------------------------------------------------------------- 182 | | Transport 183 | |-------------------------------------------------------------------------- 184 | | 185 | | Transport to be used for logging messages. You can have multiple 186 | | transports using same driver. 187 | | 188 | | Available drivers are: `file` and `console`. 189 | | 190 | */ 191 | transport: 'console', 192 | 193 | /* 194 | |-------------------------------------------------------------------------- 195 | | Console Transport 196 | |-------------------------------------------------------------------------- 197 | | 198 | | Using `console` driver for logging. This driver writes to `stdout` 199 | | and `stderr` 200 | | 201 | */ 202 | console: { 203 | driver: 'console', 204 | name: 'adonis-app', 205 | level: 'info' 206 | }, 207 | 208 | /* 209 | |-------------------------------------------------------------------------- 210 | | File Transport 211 | |-------------------------------------------------------------------------- 212 | | 213 | | File transport uses file driver and writes log messages for a given 214 | | file inside `tmp` directory for your app. 215 | | 216 | | For a different directory, set an absolute path for the filename. 217 | | 218 | */ 219 | file: { 220 | driver: 'file', 221 | name: 'adonis-app', 222 | filename: 'adonis.log', 223 | level: 'info' 224 | } 225 | }, 226 | 227 | /* 228 | |-------------------------------------------------------------------------- 229 | | Generic Cookie Options 230 | |-------------------------------------------------------------------------- 231 | | 232 | | The following cookie options are generic settings used by AdonisJs to create 233 | | cookies. However, some parts of the application like `sessions` can have 234 | | seperate settings for cookies inside `config/session.js`. 235 | | 236 | */ 237 | cookie: { 238 | httpOnly: true, 239 | sameSite: false, 240 | path: '/', 241 | maxAge: 7200 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /config/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/framework/src/Env')} */ 4 | const Env = use('Env') 5 | 6 | module.exports = { 7 | /* 8 | |-------------------------------------------------------------------------- 9 | | Authenticator 10 | |-------------------------------------------------------------------------- 11 | | 12 | | Authentication is a combination of serializer and scheme with extra 13 | | config to define on how to authenticate a user. 14 | | 15 | | Available Schemes - basic, session, jwt, api 16 | | Available Serializers - lucid, database 17 | | 18 | */ 19 | authenticator: 'session', 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Session 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Session authenticator makes use of sessions to authenticate a user. 27 | | Session authentication is always persistent. 28 | | 29 | */ 30 | session: { 31 | serializer: 'lucid', 32 | model: 'App/Models/User', 33 | scheme: 'session', 34 | uid: 'email', 35 | password: 'password' 36 | }, 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Basic Auth 41 | |-------------------------------------------------------------------------- 42 | | 43 | | The basic auth authenticator uses basic auth header to authenticate a 44 | | user. 45 | | 46 | | NOTE: 47 | | This scheme is not persistent and users are supposed to pass 48 | | login credentials on each request. 49 | | 50 | */ 51 | basic: { 52 | serializer: 'lucid', 53 | model: 'App/Models/User', 54 | scheme: 'basic', 55 | uid: 'email', 56 | password: 'password' 57 | }, 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | Jwt 62 | |-------------------------------------------------------------------------- 63 | | 64 | | The jwt authenticator works by passing a jwt token on each HTTP request 65 | | via HTTP `Authorization` header. 66 | | 67 | */ 68 | jwt: { 69 | serializer: 'lucid', 70 | model: 'App/Models/User', 71 | scheme: 'jwt', 72 | uid: 'email', 73 | password: 'password', 74 | options: { 75 | secret: Env.get('APP_KEY') 76 | } 77 | }, 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Api 82 | |-------------------------------------------------------------------------- 83 | | 84 | | The Api scheme makes use of API personal tokens to authenticate a user. 85 | | 86 | */ 87 | api: { 88 | serializer: 'lucid', 89 | model: 'App/Models/User', 90 | scheme: 'api', 91 | uid: 'email', 92 | password: 'password' 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /config/bodyParser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | /* 5 | |-------------------------------------------------------------------------- 6 | | JSON Parser 7 | |-------------------------------------------------------------------------- 8 | | 9 | | Below settings are applied when request body contains JSON payload. If 10 | | you want body parser to ignore JSON payload, then simply set `types` 11 | | to an empty array. 12 | */ 13 | json: { 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | limit 17 | |-------------------------------------------------------------------------- 18 | | 19 | | Defines the limit of JSON that can be sent by the client. If payload 20 | | is over 1mb it will not be processed. 21 | | 22 | */ 23 | limit: '1mb', 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | strict 28 | |-------------------------------------------------------------------------- 29 | | 30 | | When `scrict` is set to true, body parser will only parse Arrays and 31 | | Object. Otherwise everything parseable by `JSON.parse` is parsed. 32 | | 33 | */ 34 | strict: true, 35 | 36 | /* 37 | |-------------------------------------------------------------------------- 38 | | types 39 | |-------------------------------------------------------------------------- 40 | | 41 | | Which content types are processed as JSON payloads. You are free to 42 | | add your own types here, but the request body should be parseable 43 | | by `JSON.parse` method. 44 | | 45 | */ 46 | types: [ 47 | 'application/json', 48 | 'application/json-patch+json', 49 | 'application/vnd.api+json', 50 | 'application/csp-report' 51 | ] 52 | }, 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Raw Parser 57 | |-------------------------------------------------------------------------- 58 | | 59 | | 60 | | 61 | */ 62 | raw: { 63 | types: [ 64 | 'text/*' 65 | ] 66 | }, 67 | 68 | /* 69 | |-------------------------------------------------------------------------- 70 | | Form Parser 71 | |-------------------------------------------------------------------------- 72 | | 73 | | 74 | | 75 | */ 76 | form: { 77 | types: [ 78 | 'application/x-www-form-urlencoded' 79 | ] 80 | }, 81 | 82 | /* 83 | |-------------------------------------------------------------------------- 84 | | Files Parser 85 | |-------------------------------------------------------------------------- 86 | | 87 | | 88 | | 89 | */ 90 | files: { 91 | types: [ 92 | 'multipart/form-data' 93 | ], 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Max Size 98 | |-------------------------------------------------------------------------- 99 | | 100 | | Below value is the max size of all the files uploaded to the server. It 101 | | is validated even before files have been processed and hard exception 102 | | is thrown. 103 | | 104 | | Consider setting a reasonable value here, otherwise people may upload GB's 105 | | of files which will keep your server busy. 106 | | 107 | | Also this value is considered when `autoProcess` is set to true. 108 | | 109 | */ 110 | maxSize: '20mb', 111 | 112 | /* 113 | |-------------------------------------------------------------------------- 114 | | Auto Process 115 | |-------------------------------------------------------------------------- 116 | | 117 | | Whether or not to auto-process files. Since HTTP servers handle files via 118 | | couple of specific endpoints. It is better to set this value off and 119 | | manually process the files when required. 120 | | 121 | | This value can contain a boolean or an array of route patterns 122 | | to be autoprocessed. 123 | */ 124 | autoProcess: true, 125 | 126 | /* 127 | |-------------------------------------------------------------------------- 128 | | Process Manually 129 | |-------------------------------------------------------------------------- 130 | | 131 | | The list of routes that should not process files and instead rely on 132 | | manual process. This list should only contain routes when autoProcess 133 | | is to true. Otherwise everything is processed manually. 134 | | 135 | */ 136 | processManually: [] 137 | 138 | /* 139 | |-------------------------------------------------------------------------- 140 | | Temporary file name 141 | |-------------------------------------------------------------------------- 142 | | 143 | | Define a function, which should return a string to be used as the 144 | | tmp file name. 145 | | 146 | | If not defined, Bodyparser will use `uuid` as the tmp file name. 147 | | 148 | | To be defined as. If you are defining the function, then do make sure 149 | | to return a value from it. 150 | | 151 | | tmpFileName () { 152 | | return 'some-unique-value' 153 | | } 154 | | 155 | */ 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /config/cors.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | /* 5 | |-------------------------------------------------------------------------- 6 | | Origin 7 | |-------------------------------------------------------------------------- 8 | | 9 | | Set a list of origins to be allowed. The value can be one of the following 10 | | 11 | | Boolean: true - Allow current request origin 12 | | Boolean: false - Disallow all 13 | | String - Comma seperated list of allowed origins 14 | | Array - An array of allowed origins 15 | | String: * - A wildcard to allow current request origin 16 | | Function - Receives the current origin and should return one of the above values. 17 | | 18 | */ 19 | origin: false, 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Methods 24 | |-------------------------------------------------------------------------- 25 | | 26 | | HTTP methods to be allowed. The value can be one of the following 27 | | 28 | | String - Comma seperated list of allowed methods 29 | | Array - An array of allowed methods 30 | | 31 | */ 32 | methods: ['GET', 'PUT', 'PATCH', 'POST', 'DELETE'], 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | Headers 37 | |-------------------------------------------------------------------------- 38 | | 39 | | List of headers to be allowed via Access-Control-Request-Headers header. 40 | | The value can be on of the following. 41 | | 42 | | Boolean: true - Allow current request headers 43 | | Boolean: false - Disallow all 44 | | String - Comma seperated list of allowed headers 45 | | Array - An array of allowed headers 46 | | String: * - A wildcard to allow current request headers 47 | | Function - Receives the current header and should return one of the above values. 48 | | 49 | */ 50 | headers: true, 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Expose Headers 55 | |-------------------------------------------------------------------------- 56 | | 57 | | A list of headers to be exposed via `Access-Control-Expose-Headers` 58 | | header. The value can be on of the following. 59 | | 60 | | Boolean: false - Disallow all 61 | | String: Comma seperated list of allowed headers 62 | | Array - An array of allowed headers 63 | | 64 | */ 65 | exposeHeaders: false, 66 | 67 | /* 68 | |-------------------------------------------------------------------------- 69 | | Credentials 70 | |-------------------------------------------------------------------------- 71 | | 72 | | Define Access-Control-Allow-Credentials header. It should always be a 73 | | boolean. 74 | | 75 | */ 76 | credentials: false, 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | MaxAge 81 | |-------------------------------------------------------------------------- 82 | | 83 | | Define Access-Control-Allow-Max-Age 84 | | 85 | */ 86 | maxAge: 90 87 | } 88 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/framework/src/Env')} */ 4 | const Env = use('Env') 5 | 6 | /** @type {import('@adonisjs/ignitor/src/Helpers')} */ 7 | const Helpers = use('Helpers') 8 | 9 | module.exports = { 10 | /* 11 | |-------------------------------------------------------------------------- 12 | | Default Connection 13 | |-------------------------------------------------------------------------- 14 | | 15 | | Connection defines the default connection settings to be used while 16 | | interacting with SQL databases. 17 | | 18 | */ 19 | connection: Env.get('DB_CONNECTION', 'sqlite'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Sqlite 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Sqlite is a flat file database and can be good choice under development 27 | | environment. 28 | | 29 | | npm i --save sqlite3 30 | | 31 | */ 32 | sqlite: { 33 | client: 'sqlite3', 34 | connection: { 35 | filename: Helpers.databasePath(`${Env.get('DB_DATABASE', 'development')}.sqlite`) 36 | }, 37 | useNullAsDefault: true 38 | }, 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | MySQL 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Here we define connection settings for MySQL database. 46 | | 47 | | npm i --save mysql 48 | | 49 | */ 50 | mysql: { 51 | client: 'mysql', 52 | connection: { 53 | host: Env.get('DB_HOST', 'localhost'), 54 | port: Env.get('DB_PORT', ''), 55 | user: Env.get('DB_USER', 'root'), 56 | password: Env.get('DB_PASSWORD', ''), 57 | database: Env.get('DB_DATABASE', 'adonis') 58 | } 59 | }, 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | PostgreSQL 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Here we define connection settings for PostgreSQL database. 67 | | 68 | | npm i --save pg 69 | | 70 | */ 71 | pg: { 72 | client: 'pg', 73 | connection: { 74 | host: Env.get('DB_HOST', 'localhost'), 75 | port: Env.get('DB_PORT', ''), 76 | user: Env.get('DB_USER', 'root'), 77 | password: Env.get('DB_PASSWORD', ''), 78 | database: Env.get('DB_DATABASE', 'adonis') 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /config/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/framework/src/Env')} */ 4 | const Env = use('Env') 5 | 6 | module.exports = { 7 | /* 8 | |-------------------------------------------------------------------------- 9 | | Driver 10 | |-------------------------------------------------------------------------- 11 | | 12 | | Driver to be used for hashing values. The same driver is used by the 13 | | auth module too. 14 | | 15 | */ 16 | driver: Env.get('HASH_DRIVER', 'bcrypt'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Bcrypt 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Config related to bcrypt hashing. https://www.npmjs.com/package/bcrypt 24 | | package is used internally. 25 | | 26 | */ 27 | bcrypt: { 28 | rounds: 10 29 | }, 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Argon 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Config related to argon. https://www.npmjs.com/package/argon2 package is 37 | | used internally. 38 | | 39 | | Since argon is optional, you will have to install the dependency yourself 40 | | 41 | |============================================================================ 42 | | npm i argon2 43 | |============================================================================ 44 | | 45 | */ 46 | argon: { 47 | type: 1 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /config/session.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/framework/src/Env')} */ 4 | const Env = use('Env') 5 | 6 | module.exports = { 7 | /* 8 | |-------------------------------------------------------------------------- 9 | | Session Driver 10 | |-------------------------------------------------------------------------- 11 | | 12 | | The session driver to be used for storing session values. It can be 13 | | cookie, file or redis. 14 | | 15 | | For `redis` driver, make sure to install and register `@adonisjs/redis` 16 | | 17 | */ 18 | driver: Env.get('SESSION_DRIVER', 'cookie'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cookie Name 23 | |-------------------------------------------------------------------------- 24 | | 25 | | The name of the cookie to be used for saving session id. Session ids 26 | | are signed and encrypted. 27 | | 28 | */ 29 | cookieName: 'adonis-session', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Clear session when browser closes 34 | |-------------------------------------------------------------------------- 35 | | 36 | | If this value is true, the session cookie will be temporary and will be 37 | | removed when browser closes. 38 | | 39 | */ 40 | clearWithBrowser: true, 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Session age 45 | |-------------------------------------------------------------------------- 46 | | 47 | | This value is only used when `clearWithBrowser` is set to false. The 48 | | age must be a valid https://npmjs.org/package/ms string or should 49 | | be in milliseconds. 50 | | 51 | | Valid values are: 52 | | '2h', '10d', '5y', '2.5 hrs' 53 | | 54 | */ 55 | age: '2h', 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Cookie options 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Cookie options defines the options to be used for setting up session 63 | | cookie 64 | | 65 | */ 66 | cookie: { 67 | httpOnly: true, 68 | sameSite: false, 69 | path: '/' 70 | }, 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Sessions location 75 | |-------------------------------------------------------------------------- 76 | | 77 | | If driver is set to file, we need to define the relative location from 78 | | the temporary path or absolute url to any location. 79 | | 80 | */ 81 | file: { 82 | location: 'sessions' 83 | }, 84 | 85 | /* 86 | |-------------------------------------------------------------------------- 87 | | Redis config 88 | |-------------------------------------------------------------------------- 89 | | 90 | | The configuration for the redis driver. By default we reference it from 91 | | the redis file. But you are free to define an object here too. 92 | | 93 | */ 94 | redis: { 95 | host: '127.0.0.1', 96 | port: 6379, 97 | password: null, 98 | db: 0, 99 | keyPrefix: '' 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /config/shield.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | /* 5 | |-------------------------------------------------------------------------- 6 | | Content Security Policy 7 | |-------------------------------------------------------------------------- 8 | | 9 | | Content security policy filters out the origins not allowed to execute 10 | | and load resources like scripts, styles and fonts. There are wide 11 | | variety of options to choose from. 12 | */ 13 | csp: { 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | Directives 17 | |-------------------------------------------------------------------------- 18 | | 19 | | All directives are defined in camelCase and here is the list of 20 | | available directives and their possible values. 21 | | 22 | | https://content-security-policy.com 23 | | 24 | | @example 25 | | directives: { 26 | | defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com'] 27 | | } 28 | | 29 | */ 30 | directives: { 31 | }, 32 | /* 33 | |-------------------------------------------------------------------------- 34 | | Report only 35 | |-------------------------------------------------------------------------- 36 | | 37 | | Setting `reportOnly=true` will not block the scripts from running and 38 | | instead report them to a URL. 39 | | 40 | */ 41 | reportOnly: false, 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Set all headers 45 | |-------------------------------------------------------------------------- 46 | | 47 | | Headers staring with `X` have been depreciated, since all major browsers 48 | | supports the standard CSP header. So its better to disable deperciated 49 | | headers, unless you want them to be set. 50 | | 51 | */ 52 | setAllHeaders: false, 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Disable on android 57 | |-------------------------------------------------------------------------- 58 | | 59 | | Certain versions of android are buggy with CSP policy. So you can set 60 | | this value to true, to disable it for Android versions with buggy 61 | | behavior. 62 | | 63 | | Here is an issue reported on a different package, but helpful to read 64 | | if you want to know the behavior. https://github.com/helmetjs/helmet/pull/82 65 | | 66 | */ 67 | disableAndroid: true 68 | }, 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | X-XSS-Protection 73 | |-------------------------------------------------------------------------- 74 | | 75 | | X-XSS Protection saves applications from XSS attacks. It is adopted 76 | | by IE and later followed by some other browsers. 77 | | 78 | | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection 79 | | 80 | */ 81 | xss: { 82 | enabled: true, 83 | enableOnOldIE: false 84 | }, 85 | 86 | /* 87 | |-------------------------------------------------------------------------- 88 | | Iframe Options 89 | |-------------------------------------------------------------------------- 90 | | 91 | | xframe defines whether or not your website can be embedded inside an 92 | | iframe. Choose from one of the following options. 93 | | @available options 94 | | DENY, SAMEORIGIN, ALLOW-FROM http://example.com 95 | | 96 | | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options 97 | */ 98 | xframe: 'DENY', 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | No Sniff 103 | |-------------------------------------------------------------------------- 104 | | 105 | | Browsers have a habit of sniffing content-type of a response. Which means 106 | | files with .txt extension containing Javascript code will be executed as 107 | | Javascript. You can disable this behavior by setting nosniff to false. 108 | | 109 | | Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options 110 | | 111 | */ 112 | nosniff: true, 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | No Open 117 | |-------------------------------------------------------------------------- 118 | | 119 | | IE users can execute webpages in the context of your website, which is 120 | | a serious security risk. Below option will manage this for you. 121 | | 122 | */ 123 | noopen: true, 124 | 125 | /* 126 | |-------------------------------------------------------------------------- 127 | | CSRF Protection 128 | |-------------------------------------------------------------------------- 129 | | 130 | | CSRF Protection adds another layer of security by making sure, actionable 131 | | routes does have a valid token to execute an action. 132 | | 133 | */ 134 | csrf: { 135 | enable: true, 136 | methods: ['POST', 'PUT', 'DELETE'], 137 | filterUris: [], 138 | cookieOptions: { 139 | httpOnly: false, 140 | sameSite: true, 141 | path: '/', 142 | maxAge: 7200 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /database/factory.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Factory 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Factories are used to define blueprints for database tables or Lucid 9 | | models. Later you can use these blueprints to seed your database 10 | | with dummy data. 11 | | 12 | */ 13 | 14 | /** @type {import('@adonisjs/lucid/src/Factory')} */ 15 | // const Factory = use('Factory') 16 | 17 | // Factory.blueprint('App/Models/User', (faker) => { 18 | // return { 19 | // username: faker.username() 20 | // } 21 | // }) 22 | -------------------------------------------------------------------------------- /database/migrations/1503248427885_user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/lucid/src/Schema')} */ 4 | const Schema = use('Schema') 5 | 6 | class UserSchema extends Schema { 7 | up () { 8 | this.create('users', (table) => { 9 | table.increments() 10 | table.string('username', 80).notNullable().unique() 11 | table.string('email', 254).notNullable().unique() 12 | table.string('password', 60).notNullable() 13 | table.timestamps() 14 | }) 15 | } 16 | 17 | down () { 18 | this.drop('users') 19 | } 20 | } 21 | 22 | module.exports = UserSchema 23 | -------------------------------------------------------------------------------- /database/migrations/1503248427886_token.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** @type {import('@adonisjs/lucid/src/Schema')} */ 4 | const Schema = use('Schema') 5 | 6 | class TokensSchema extends Schema { 7 | up () { 8 | this.create('tokens', (table) => { 9 | table.increments() 10 | table.integer('user_id').unsigned().references('id').inTable('users') 11 | table.string('token', 255).notNullable().unique().index() 12 | table.string('type', 80).notNullable() 13 | table.boolean('is_revoked').defaultTo(false) 14 | table.timestamps() 15 | }) 16 | } 17 | 18 | down () { 19 | this.drop('tokens') 20 | } 21 | } 22 | 23 | module.exports = TokensSchema 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adonis-fullstack-app", 3 | "version": "4.1.0", 4 | "adonis-version": "4.1.0", 5 | "description": "The fullstack application boilerplate for Adonisjs", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "node server.js", 9 | "test": "node ace test" 10 | }, 11 | "keywords": [ 12 | "adonisjs", 13 | "adonis-app" 14 | ], 15 | "author": "", 16 | "license": "UNLICENSED", 17 | "private": true, 18 | "dependencies": { 19 | "@adonisjs/ace": "^5.0.8", 20 | "@adonisjs/auth": "^3.0.7", 21 | "@adonisjs/bodyparser": "~2.0.9", 22 | "@adonisjs/cors": "^1.0.7", 23 | "@adonisjs/fold": "^4.0.9", 24 | "@adonisjs/framework": "^5.0.9", 25 | "@adonisjs/ignitor": "^2.0.8", 26 | "@adonisjs/lucid": "^6.1.3", 27 | "@adonisjs/session": "^1.0.27", 28 | "@adonisjs/shield": "^1.0.8" 29 | }, 30 | "devDependencies": {}, 31 | "autoload": { 32 | "App": "./app" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/pyramid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/adonis-fullstack-app/ad11b7abec493d617e2d7e546b68ff44aa2484e5/public/pyramid.png -------------------------------------------------------------------------------- /public/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/adonis-fullstack-app/ad11b7abec493d617e2d7e546b68ff44aa2484e5/public/splash.png -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Montserrat:300'); 2 | 3 | html, body { 4 | height: 100%; 5 | width: 100%; 6 | } 7 | 8 | body { 9 | font-family: 'Montserrat', sans-serif; 10 | font-weight: 300; 11 | background-image: url("/splash.png"); 12 | background-color: #220052; 13 | } 14 | 15 | * { 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | section { 21 | height: 100%; 22 | display: flex; 23 | flex-direction: column; 24 | justify-content: center; 25 | align-items: center; 26 | max-width: 536px; 27 | margin: auto; 28 | position: relative; 29 | } 30 | 31 | section:before { 32 | content: ""; 33 | position: absolute; 34 | background: url("/pyramid.png") no-repeat; 35 | background-size: 100%; 36 | width: 100%; 37 | height: 402px; 38 | z-index: -1; 39 | } 40 | 41 | .logo { 42 | background: url("/logo.svg") no-repeat; 43 | width: 36px; 44 | height: 33px; 45 | background-size: 100%; 46 | margin-bottom: 35px; 47 | opacity: 0; 48 | animation: slideUp 1s cubic-bezier(0.19, 1, 0.30, 1) 1.3s forwards; 49 | } 50 | 51 | .title { 52 | background: url("/title.svg") no-repeat; 53 | width: 219px; 54 | height: 36px; 55 | background-size: 100%; 56 | opacity: 0; 57 | animation: slideUp 1s cubic-bezier(0.19, 1, 0.30, 1) 0.2s forwards; 58 | } 59 | 60 | .subtitle { 61 | margin-top: 25px; 62 | color: #BDB3CB; 63 | font-size: 17px; 64 | text-align: center; 65 | letter-spacing: 0.5; 66 | opacity: 0; 67 | animation: slideUp 1s cubic-bezier(0.19, 1, 0.30, 1) 0.5s forwards; 68 | } 69 | 70 | 71 | a { 72 | color: inherit; 73 | text-decoration: underline; 74 | } 75 | 76 | p { 77 | margin: 0.83rem 0; 78 | } 79 | 80 | @keyframes slideUp { 81 | 0% { 82 | transform: translateY(40px); 83 | opacity: 0; 84 | } 85 | 50% { 86 | opacity: 0.2%; 87 | } 88 | 100% { 89 | opacity: 1; 90 | transform: none; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /public/title.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/welcome.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |AdonisJs simplicity will make you feel confident about your code
14 |15 | Don't know where to start? Read the documentation. 16 |
17 |