commit 05a6705db983d8f03e446c0543da5c53a2862a12 Author: Sagi Dayan Date: Mon Jan 13 20:57:43 2020 -0500 Initial commit 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 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 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..14cf2b1 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Seepur + +#### A shared story time experience \ No newline at end of file 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 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 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 + } +} 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' + } +} 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' + | } + | + */ + } +} 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 +} 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') + } + } +} 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 + } +} 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: '' + } +} 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 + } + } +} 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/package.json b/package.json new file mode 100644 index 0000000..00f6deb --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "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" + }, + "devDependencies": {}, + "autoload": { + "App": "./app" + } +} 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 @@ + 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 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; + } +} 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 @@ + 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 @@ + + + + + Hello Adonis + {{ style('style') }} + + +
+ +
+
+

AdonisJs simplicity will make you feel confident about your code

+

+ Don't know where to start? Read the documentation. +

+
+
+ + 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) 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 } 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) diff --git a/start/routes.js b/start/routes.js new file mode 100644 index 0000000..2c057eb --- /dev/null +++ b/start/routes.js @@ -0,0 +1,19 @@ +'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') + +Route.on('/').render('welcome')