├── .adonisrc.json ├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── ace ├── ace-manifest.json ├── app └── Exceptions │ └── Handler.ts ├── commands └── index.ts ├── config ├── app.ts ├── bodyparser.ts ├── cors.ts └── hash.ts ├── contracts ├── env.ts ├── events.ts └── hash.ts ├── env.ts ├── lambda.ts ├── package-lock.json ├── package.json ├── providers └── AppProvider.ts ├── server.ts ├── serverless.aws.yml ├── start ├── kernel.ts └── routes.ts └── tsconfig.json /.adonisrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript": true, 3 | "commands": ["./commands", "@adonisjs/core/commands", "@adonisjs/repl/build/commands"], 4 | "exceptionHandlerNamespace": "App/Exceptions/Handler", 5 | "aliases": { 6 | "App": "app", 7 | "Config": "config", 8 | "Database": "database", 9 | "Contracts": "contracts" 10 | }, 11 | "preloads": ["./start/routes", "./start/kernel"], 12 | "providers": ["./providers/AppProvider", "@adonisjs/core"], 13 | "aceProviders": ["@adonisjs/repl"] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | [*] 3 | indent_style = space 4 | indent_size = 2 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.json] 11 | insert_final_newline = ignore 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PORT=3333 2 | HOST=0.0.0.0 3 | NODE_ENV=development 4 | APP_KEY=TVTj8OCKxhHhHWeyZHkVhK1TPL7ghIK9 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:adonis/typescriptApp", "prettier"], 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": ["error"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .build 4 | coverage 5 | .vscode 6 | .DS_STORE 7 | .env 8 | tmp 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true, 5 | "useTabs": false, 6 | "quoteProps": "consistent", 7 | "bracketSpacing": true, 8 | "arrowParens": "always", 9 | "printWidth": 100 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # adonis-serverless 2 | 3 | This is an example repository for running [AdonisJS](https://github.com/adonisjs) on AWS Lambda. Work on this project has been done by [@satheler](https://github.com/satheler) with his package [s12r](https://github.com/satheler/s12r) and [@capopovici](https://github.com/capopovici) by making it compatible with version [5.1.9](https://github.com/adonisjs/core/releases/tag/v5.1.9) of AdonisJS. 4 | 5 | ## Installation 6 | 7 | ### Install the [s12r](https://github.com/satheler/s12r) package. 8 | 9 | Using yarn: 10 | ```bash 11 | yarn add @satheler/s12r 12 | ``` 13 | 14 | Using npm: 15 | ```bash 16 | npm install @satheler/s12r 17 | ``` 18 | 19 | ### Install the [serverless-plugin-typescript]() serverless plugin. 20 | 21 | Using yarn: 22 | ```bash 23 | yarn add serverless-plugin-typescript 24 | ``` 25 | 26 | Using npm: 27 | ```bash 28 | npm install serverless-plugin-typescript 29 | ``` 30 | 31 | ### Add custom Lambda handler. 32 | 33 | Copy [lambda.ts](https://github.com/tomhatzer/adonis-serverless/blob/master/lambda.ts) to your project. 34 | 35 | ### Update your serverless configuration. 36 | 37 | If you do not have a serverless.yml configuration already, you can copy the one from the repository and change a few values like: 38 | 39 | - service 40 | - region 41 | - stage 42 | - environment variables 43 | 44 | ### Update your tsconfig.json file. 45 | 46 | Add additional `compilerOptions`: 47 | 48 | ``` 49 | "baseUrl": ".", 50 | "preserveConstEnums": true, 51 | "strictNullChecks": true, 52 | "target": "ESNext", 53 | "moduleResolution": "node", 54 | "module": "commonjs", 55 | "allowSyntheticDefaultImports": true, 56 | "emitDecoratorMetadata": true, 57 | "experimentalDecorators": true, 58 | "esModuleInterop": true, 59 | "noUnusedLocals": true, 60 | "skipLibCheck": true, 61 | "noUnusedParameters": true, 62 | "removeComments": true, 63 | "declaration": false, 64 | "lib": [ 65 | "ESNext" 66 | ], 67 | "outDir": ".build", 68 | ``` 69 | 70 | ## Credits 71 | 72 | [@satheler](https://github.com/satheler) 73 | [@capopovici](https://github.com/capopovici) -------------------------------------------------------------------------------- /ace: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Ace Commands 4 | |-------------------------------------------------------------------------- 5 | | 6 | | This file is the entry point for running ace commands. 7 | | 8 | */ 9 | 10 | require('reflect-metadata') 11 | require('source-map-support').install({ handleUncaughtExceptions: false }) 12 | 13 | const { Ignitor } = require('@adonisjs/core/build/standalone') 14 | new Ignitor(__dirname) 15 | .ace() 16 | .handle(process.argv.slice(2)) 17 | -------------------------------------------------------------------------------- /ace-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": { 3 | "dump:rcfile": { 4 | "settings": {}, 5 | "commandPath": "@adonisjs/core/commands/DumpRc", 6 | "commandName": "dump:rcfile", 7 | "description": "Dump contents of .adonisrc.json file along with defaults", 8 | "args": [], 9 | "aliases": [], 10 | "flags": [] 11 | }, 12 | "list:routes": { 13 | "settings": { 14 | "loadApp": true 15 | }, 16 | "commandPath": "@adonisjs/core/commands/ListRoutes", 17 | "commandName": "list:routes", 18 | "description": "List application routes", 19 | "args": [], 20 | "aliases": [], 21 | "flags": [ 22 | { 23 | "name": "json", 24 | "propertyName": "json", 25 | "type": "boolean", 26 | "description": "Output as JSON" 27 | } 28 | ] 29 | }, 30 | "generate:key": { 31 | "settings": {}, 32 | "commandPath": "@adonisjs/core/commands/GenerateKey", 33 | "commandName": "generate:key", 34 | "description": "Generate a new APP_KEY secret", 35 | "args": [], 36 | "aliases": [], 37 | "flags": [] 38 | }, 39 | "repl": { 40 | "settings": { 41 | "loadApp": true, 42 | "environment": "repl", 43 | "stayAlive": true 44 | }, 45 | "commandPath": "@adonisjs/repl/build/commands/AdonisRepl", 46 | "commandName": "repl", 47 | "description": "Start a new REPL session", 48 | "args": [], 49 | "aliases": [], 50 | "flags": [] 51 | } 52 | }, 53 | "aliases": {} 54 | } 55 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Http Exception Handler 4 | |-------------------------------------------------------------------------- 5 | | 6 | | AdonisJs will forward all exceptions occurred during an HTTP request to 7 | | the following class. You can learn more about exception handling by 8 | | reading docs. 9 | | 10 | | The exception handler extends a base `HttpExceptionHandler` which is not 11 | | mandatory, however it can do lot of heavy lifting to handle the errors 12 | | properly. 13 | | 14 | */ 15 | 16 | import Logger from '@ioc:Adonis/Core/Logger' 17 | import HttpExceptionHandler from '@ioc:Adonis/Core/HttpExceptionHandler' 18 | 19 | export default class ExceptionHandler extends HttpExceptionHandler { 20 | constructor() { 21 | super(Logger) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /commands/index.ts: -------------------------------------------------------------------------------- 1 | import { listDirectoryFiles } from '@adonisjs/core/build/standalone' 2 | import Application from '@ioc:Adonis/Core/Application' 3 | 4 | /* 5 | |-------------------------------------------------------------------------- 6 | | Exporting an array of commands 7 | |-------------------------------------------------------------------------- 8 | | 9 | | Instead of manually exporting each file from this directory, we use the 10 | | helper `listDirectoryFiles` to recursively collect and export an array 11 | | of filenames. 12 | | 13 | | Couple of things to note: 14 | | 15 | | 1. The file path must be relative from the project root and not this directory. 16 | | 2. We must ignore this file to avoid getting into an infinite loop 17 | | 18 | */ 19 | export default listDirectoryFiles(__dirname, Application.appRoot, ['./commands/index']) 20 | -------------------------------------------------------------------------------- /config/app.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Config source: https://git.io/JfefZ 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this config 5 | * file. 6 | */ 7 | 8 | import proxyAddr from 'proxy-addr' 9 | import Env from '@ioc:Adonis/Core/Env' 10 | import { ServerConfig } from '@ioc:Adonis/Core/Server' 11 | import { LoggerConfig } from '@ioc:Adonis/Core/Logger' 12 | import { ProfilerConfig } from '@ioc:Adonis/Core/Profiler' 13 | import { ValidatorConfig } from '@ioc:Adonis/Core/Validator' 14 | 15 | /* 16 | |-------------------------------------------------------------------------- 17 | | Application secret key 18 | |-------------------------------------------------------------------------- 19 | | 20 | | The secret to encrypt and sign different values in your application. 21 | | Make sure to keep the `APP_KEY` as an environment variable and secure. 22 | | 23 | | Note: Changing the application key for an existing app will make all 24 | | the cookies invalid and also the existing encrypted data will not 25 | | be decrypted. 26 | | 27 | */ 28 | export const appKey: string = Env.get('APP_KEY') 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Http server configuration 33 | |-------------------------------------------------------------------------- 34 | | 35 | | The configuration for the HTTP(s) server. Make sure to go through all 36 | | the config properties to make keep server secure. 37 | | 38 | */ 39 | export const http: ServerConfig = { 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Allow method spoofing 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Method spoofing enables defining custom HTTP methods using a query string 46 | | `_method`. This is usually required when you are making traditional 47 | | form requests and wants to use HTTP verbs like `PUT`, `DELETE` and 48 | | so on. 49 | | 50 | */ 51 | allowMethodSpoofing: false, 52 | 53 | /* 54 | |-------------------------------------------------------------------------- 55 | | Subdomain offset 56 | |-------------------------------------------------------------------------- 57 | */ 58 | subdomainOffset: 2, 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Request Ids 63 | |-------------------------------------------------------------------------- 64 | | 65 | | Setting this value to `true` will generate a unique request id for each 66 | | HTTP request and set it as `x-request-id` header. 67 | | 68 | */ 69 | generateRequestId: false, 70 | 71 | /* 72 | |-------------------------------------------------------------------------- 73 | | Trusting proxy servers 74 | |-------------------------------------------------------------------------- 75 | | 76 | | Define the proxy servers that AdonisJs must trust for reading `X-Forwarded` 77 | | headers. 78 | | 79 | */ 80 | trustProxy: proxyAddr.compile('loopback'), 81 | 82 | /* 83 | |-------------------------------------------------------------------------- 84 | | Generating Etag 85 | |-------------------------------------------------------------------------- 86 | | 87 | | Whether or not to generate an etag for every response. 88 | | 89 | */ 90 | etag: false, 91 | 92 | /* 93 | |-------------------------------------------------------------------------- 94 | | JSONP Callback 95 | |-------------------------------------------------------------------------- 96 | */ 97 | jsonpCallbackName: 'callback', 98 | 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Cookie settings 102 | |-------------------------------------------------------------------------- 103 | */ 104 | cookie: { 105 | domain: '', 106 | path: '/', 107 | maxAge: '2h', 108 | httpOnly: true, 109 | secure: false, 110 | sameSite: false, 111 | }, 112 | 113 | /* 114 | |-------------------------------------------------------------------------- 115 | | Force Content Negotiation 116 | |-------------------------------------------------------------------------- 117 | | 118 | | The internals of the framework relies on the content negotiation to 119 | | detect the best possible response type for a given HTTP request. 120 | | 121 | | However, it is a very common these days that API servers always wants to 122 | | make response in JSON regardless of the existence of the `Accept` header. 123 | | 124 | | By setting `forceContentNegotiationTo = 'application/json'`, you negotiate 125 | | with the server in advance to always return JSON without relying on the 126 | | client to set the header explicitly. 127 | | 128 | */ 129 | forceContentNegotiationTo: 'application/json', 130 | } 131 | 132 | /* 133 | |-------------------------------------------------------------------------- 134 | | Logger 135 | |-------------------------------------------------------------------------- 136 | */ 137 | export const logger: LoggerConfig = { 138 | /* 139 | |-------------------------------------------------------------------------- 140 | | Application name 141 | |-------------------------------------------------------------------------- 142 | | 143 | | The name of the application you want to add to the log. It is recommended 144 | | to always have app name in every log line. 145 | | 146 | | The `APP_NAME` environment variable is automatically set by AdonisJS by 147 | | reading the `name` property from the `package.json` file. 148 | | 149 | */ 150 | name: Env.get('APP_NAME'), 151 | 152 | /* 153 | |-------------------------------------------------------------------------- 154 | | Toggle logger 155 | |-------------------------------------------------------------------------- 156 | | 157 | | Enable or disable logger application wide 158 | | 159 | */ 160 | enabled: true, 161 | 162 | /* 163 | |-------------------------------------------------------------------------- 164 | | Logging level 165 | |-------------------------------------------------------------------------- 166 | | 167 | | The level from which you want the logger to flush logs. It is recommended 168 | | to make use of the environment variable, so that you can define log levels 169 | | at deployment level and not code level. 170 | | 171 | */ 172 | level: Env.get('LOG_LEVEL', 'info'), 173 | 174 | /* 175 | |-------------------------------------------------------------------------- 176 | | Pretty print 177 | |-------------------------------------------------------------------------- 178 | | 179 | | It is highly advised NOT to use `prettyPrint` in production, since it 180 | | can have huge impact on performance. 181 | | 182 | */ 183 | prettyPrint: Env.get('NODE_ENV') === 'development', 184 | } 185 | 186 | /* 187 | |-------------------------------------------------------------------------- 188 | | Profiler 189 | |-------------------------------------------------------------------------- 190 | */ 191 | export const profiler: ProfilerConfig = { 192 | /* 193 | |-------------------------------------------------------------------------- 194 | | Toggle profiler 195 | |-------------------------------------------------------------------------- 196 | | 197 | | Enable or disable profiler 198 | | 199 | */ 200 | enabled: true, 201 | 202 | /* 203 | |-------------------------------------------------------------------------- 204 | | Blacklist actions/row labels 205 | |-------------------------------------------------------------------------- 206 | | 207 | | Define an array of actions or row labels that you want to disable from 208 | | getting profiled. 209 | | 210 | */ 211 | blacklist: [], 212 | 213 | /* 214 | |-------------------------------------------------------------------------- 215 | | Whitelist actions/row labels 216 | |-------------------------------------------------------------------------- 217 | | 218 | | Define an array of actions or row labels that you want to whitelist for 219 | | the profiler. When whitelist is defined, then `blacklist` is ignored. 220 | | 221 | */ 222 | whitelist: [], 223 | } 224 | 225 | /* 226 | |-------------------------------------------------------------------------- 227 | | Validator 228 | |-------------------------------------------------------------------------- 229 | | 230 | | Configure the global configuration for the validator. Here's the reference 231 | | to the default config https://git.io/JT0WE 232 | | 233 | */ 234 | export const validator: ValidatorConfig = {} 235 | -------------------------------------------------------------------------------- /config/bodyparser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Config source: https://git.io/Jfefn 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this config 5 | * file. 6 | */ 7 | 8 | import { BodyParserConfig } from '@ioc:Adonis/Core/BodyParser' 9 | 10 | const bodyParserConfig: BodyParserConfig = { 11 | /* 12 | |-------------------------------------------------------------------------- 13 | | White listed methods 14 | |-------------------------------------------------------------------------- 15 | | 16 | | HTTP methods for which body parsing must be performed. It is a good practice 17 | | to avoid body parsing for `GET` requests. 18 | | 19 | */ 20 | whitelistedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'], 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | JSON parser settings 25 | |-------------------------------------------------------------------------- 26 | | 27 | | The settings for the JSON parser. The types defines the request content 28 | | types which gets processed by the JSON parser. 29 | | 30 | */ 31 | json: { 32 | encoding: 'utf-8', 33 | limit: '1mb', 34 | strict: true, 35 | types: [ 36 | 'application/json', 37 | 'application/json-patch+json', 38 | 'application/vnd.api+json', 39 | 'application/csp-report', 40 | ], 41 | }, 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Form parser settings 46 | |-------------------------------------------------------------------------- 47 | | 48 | | The settings for the `application/x-www-form-urlencoded` parser. The types 49 | | defines the request content types which gets processed by the form parser. 50 | | 51 | */ 52 | form: { 53 | encoding: 'utf-8', 54 | limit: '1mb', 55 | queryString: {}, 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Convert empty strings to null 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Convert empty form fields to null. HTML forms results in field string 63 | | value when the field is left blank. This option normalizes all the blank 64 | | field values to "null" 65 | | 66 | */ 67 | convertEmptyStringsToNull: true, 68 | 69 | types: ['application/x-www-form-urlencoded'], 70 | }, 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Raw body parser settings 75 | |-------------------------------------------------------------------------- 76 | | 77 | | Raw body just reads the request body stream as a plain text, which you 78 | | can process by hand. This must be used when request body type is not 79 | | supported by the body parser. 80 | | 81 | */ 82 | raw: { 83 | encoding: 'utf-8', 84 | limit: '1mb', 85 | queryString: {}, 86 | types: ['text/*'], 87 | }, 88 | 89 | /* 90 | |-------------------------------------------------------------------------- 91 | | Multipart parser settings 92 | |-------------------------------------------------------------------------- 93 | | 94 | | The settings for the `multipart/form-data` parser. The types defines the 95 | | request content types which gets processed by the form parser. 96 | | 97 | */ 98 | multipart: { 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Auto process 102 | |-------------------------------------------------------------------------- 103 | | 104 | | The auto process option will process uploaded files and writes them to 105 | | the `tmp` folder. You can turn it off and then manually use the stream 106 | | to pipe stream to a different destination. 107 | | 108 | | It is recommended to keep `autoProcess=true`. Unless you are processing bigger 109 | | file sizes. 110 | | 111 | */ 112 | autoProcess: true, 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Files to be processed manually 117 | |-------------------------------------------------------------------------- 118 | | 119 | | You can turn off `autoProcess` for certain routes by defining 120 | | routes inside the following array. 121 | | 122 | | NOTE: Make sure the route pattern starts with a leading slash. 123 | | 124 | | Correct 125 | | ```js 126 | | /projects/:id/file 127 | | ``` 128 | | 129 | | Incorrect 130 | | ```js 131 | | projects/:id/file 132 | | ``` 133 | */ 134 | processManually: [], 135 | 136 | /* 137 | |-------------------------------------------------------------------------- 138 | | Temporary file name 139 | |-------------------------------------------------------------------------- 140 | | 141 | | When auto processing is on. We will use this method to compute the temporary 142 | | file name. AdonisJs will compute a unique `tmpPath` for you automatically, 143 | | However, you can also define your own custom method. 144 | | 145 | */ 146 | // tmpFileName () { 147 | // }, 148 | 149 | /* 150 | |-------------------------------------------------------------------------- 151 | | Encoding 152 | |-------------------------------------------------------------------------- 153 | | 154 | | Request body encoding 155 | | 156 | */ 157 | encoding: 'utf-8', 158 | 159 | /* 160 | |-------------------------------------------------------------------------- 161 | | Convert empty strings to null 162 | |-------------------------------------------------------------------------- 163 | | 164 | | Convert empty form fields to null. HTML forms results in field string 165 | | value when the field is left blank. This option normalizes all the blank 166 | | field values to "null" 167 | | 168 | */ 169 | convertEmptyStringsToNull: true, 170 | 171 | /* 172 | |-------------------------------------------------------------------------- 173 | | Max Fields 174 | |-------------------------------------------------------------------------- 175 | | 176 | | The maximum number of fields allowed in the request body. The field includes 177 | | text inputs and files both. 178 | | 179 | */ 180 | maxFields: 1000, 181 | 182 | /* 183 | |-------------------------------------------------------------------------- 184 | | Request body limit 185 | |-------------------------------------------------------------------------- 186 | | 187 | | The total limit to the multipart body. This includes all request files 188 | | and fields data. 189 | | 190 | */ 191 | limit: '20mb', 192 | 193 | /* 194 | |-------------------------------------------------------------------------- 195 | | Types 196 | |-------------------------------------------------------------------------- 197 | | 198 | | The types that will be considered and parsed as multipart body. 199 | | 200 | */ 201 | types: ['multipart/form-data'], 202 | }, 203 | } 204 | 205 | export default bodyParserConfig 206 | -------------------------------------------------------------------------------- /config/cors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Config source: https://git.io/JfefC 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this config 5 | * file. 6 | */ 7 | 8 | import { CorsConfig } from '@ioc:Adonis/Core/Cors' 9 | 10 | const corsConfig: CorsConfig = { 11 | /* 12 | |-------------------------------------------------------------------------- 13 | | Enabled 14 | |-------------------------------------------------------------------------- 15 | | 16 | | A boolean to enable or disable CORS integration from your AdonisJs 17 | | application. 18 | | 19 | | Setting the value to `true` will enable the CORS for all HTTP request. However, 20 | | you can define a function to enable/disable it on per request basis as well. 21 | | 22 | */ 23 | enabled: false, 24 | 25 | // You can also use a function that return true or false. 26 | // enabled: (request) => request.url().startsWith('/api') 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Origin 31 | |-------------------------------------------------------------------------- 32 | | 33 | | Set a list of origins to be allowed for `Access-Control-Allow-Origin`. 34 | | The value can be one of the following: 35 | | 36 | | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin 37 | | 38 | | Boolean (true) - Allow current request origin. 39 | | Boolean (false) - Disallow all. 40 | | String - Comma separated list of allowed origins. 41 | | Array - An array of allowed origins. 42 | | String (*) - A wildcard (*) to allow all request origins. 43 | | Function - Receives the current origin string and should return 44 | | one of the above values. 45 | | 46 | */ 47 | origin: true, 48 | 49 | /* 50 | |-------------------------------------------------------------------------- 51 | | Methods 52 | |-------------------------------------------------------------------------- 53 | | 54 | | An array of allowed HTTP methods for CORS. The `Access-Control-Request-Method` 55 | | is checked against the following list. 56 | | 57 | | Following is the list of default methods. Feel free to add more. 58 | */ 59 | methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'], 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Headers 64 | |-------------------------------------------------------------------------- 65 | | 66 | | List of headers to be allowed for `Access-Control-Allow-Headers` header. 67 | | The value can be one of the following: 68 | | 69 | | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers 70 | | 71 | | Boolean(true) - Allow all headers mentioned in `Access-Control-Request-Headers`. 72 | | Boolean(false) - Disallow all headers. 73 | | String - Comma separated list of allowed headers. 74 | | Array - An array of allowed headers. 75 | | Function - Receives the current header and should return one of the above values. 76 | | 77 | */ 78 | headers: true, 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Expose Headers 83 | |-------------------------------------------------------------------------- 84 | | 85 | | A list of headers to be exposed by setting `Access-Control-Expose-Headers`. 86 | | header. By default following 6 simple response headers are exposed. 87 | | 88 | | Cache-Control 89 | | Content-Language 90 | | Content-Type 91 | | Expires 92 | | Last-Modified 93 | | Pragma 94 | | 95 | | In order to add more headers, simply define them inside the following array. 96 | | 97 | | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers 98 | | 99 | */ 100 | exposeHeaders: [ 101 | 'cache-control', 102 | 'content-language', 103 | 'content-type', 104 | 'expires', 105 | 'last-modified', 106 | 'pragma', 107 | ], 108 | 109 | /* 110 | |-------------------------------------------------------------------------- 111 | | Credentials 112 | |-------------------------------------------------------------------------- 113 | | 114 | | Toggle `Access-Control-Allow-Credentials` header. If value is set to `true`, 115 | | then header will be set, otherwise not. 116 | | 117 | | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials 118 | | 119 | */ 120 | credentials: true, 121 | 122 | /* 123 | |-------------------------------------------------------------------------- 124 | | MaxAge 125 | |-------------------------------------------------------------------------- 126 | | 127 | | Define `Access-Control-Max-Age` header in seconds. 128 | | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age 129 | | 130 | */ 131 | maxAge: 90, 132 | } 133 | 134 | export default corsConfig 135 | -------------------------------------------------------------------------------- /config/hash.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Config source: https://git.io/JfefW 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this config 5 | * file. 6 | */ 7 | 8 | import Env from '@ioc:Adonis/Core/Env' 9 | import { HashConfig } from '@ioc:Adonis/Core/Hash' 10 | 11 | /* 12 | |-------------------------------------------------------------------------- 13 | | Hash Config 14 | |-------------------------------------------------------------------------- 15 | | 16 | | The `HashConfig` relies on the `HashList` interface which is 17 | | defined inside `contracts` directory. 18 | | 19 | */ 20 | const hashConfig: HashConfig = { 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Default hasher 24 | |-------------------------------------------------------------------------- 25 | | 26 | | By default we make use of the bcrypt hasher to hash values. However, feel 27 | | free to change the default value 28 | | 29 | */ 30 | default: Env.get('HASH_DRIVER', 'argon'), 31 | 32 | list: { 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Argon 36 | |-------------------------------------------------------------------------- 37 | | 38 | | Argon mapping uses the `argon2` driver to hash values. 39 | | 40 | | Make sure you install the underlying dependency for this driver to work. 41 | | https://www.npmjs.com/package/phc-argon2. 42 | | 43 | | npm install phc-argon2 44 | | 45 | */ 46 | argon: { 47 | driver: 'argon2', 48 | variant: 'id', 49 | iterations: 3, 50 | memory: 4096, 51 | parallelism: 1, 52 | saltSize: 16, 53 | }, 54 | 55 | /* 56 | |-------------------------------------------------------------------------- 57 | | Bcrypt 58 | |-------------------------------------------------------------------------- 59 | | 60 | | Bcrypt mapping uses the `bcrypt` driver to hash values. 61 | | 62 | | Make sure you install the underlying dependency for this driver to work. 63 | | https://www.npmjs.com/package/phc-bcrypt. 64 | | 65 | | npm install phc-bcrypt 66 | | 67 | */ 68 | bcrypt: { 69 | driver: 'bcrypt', 70 | rounds: 10, 71 | }, 72 | }, 73 | } 74 | 75 | export default hashConfig 76 | -------------------------------------------------------------------------------- /contracts/env.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Contract source: https://git.io/JTm6U 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this contract 5 | * file. 6 | */ 7 | 8 | declare module '@ioc:Adonis/Core/Env' { 9 | /* 10 | |-------------------------------------------------------------------------- 11 | | Getting types for validated environment variables 12 | |-------------------------------------------------------------------------- 13 | | 14 | | The `default` export from the "../env.ts" file exports types for the 15 | | validated environment variables. Here we merge them with the `EnvTypes` 16 | | interface so that you can enjoy intellisense when using the "Env" 17 | | module. 18 | | 19 | */ 20 | 21 | type CustomTypes = typeof import('../env').default 22 | interface EnvTypes extends CustomTypes {} 23 | } 24 | -------------------------------------------------------------------------------- /contracts/events.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Contract source: https://git.io/JfefG 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this contract 5 | * file. 6 | */ 7 | 8 | declare module '@ioc:Adonis/Core/Event' { 9 | /* 10 | |-------------------------------------------------------------------------- 11 | | Define typed events 12 | |-------------------------------------------------------------------------- 13 | | 14 | | You can define types for events inside the following interface and 15 | | AdonisJS will make sure that all listeners and emit calls adheres 16 | | to the defined types. 17 | | 18 | | For example: 19 | | 20 | | interface EventsList { 21 | | 'new:user': UserModel 22 | | } 23 | | 24 | | Now calling `Event.emit('new:user')` will statically ensure that passed value is 25 | | an instance of the the UserModel only. 26 | | 27 | */ 28 | interface EventsList {} 29 | } 30 | -------------------------------------------------------------------------------- /contracts/hash.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Contract source: https://git.io/Jfefs 3 | * 4 | * Feel free to let us know via PR, if you find something broken in this contract 5 | * file. 6 | */ 7 | 8 | declare module '@ioc:Adonis/Core/Hash' { 9 | interface HashersList { 10 | bcrypt: { 11 | config: BcryptConfig 12 | implementation: BcryptContract 13 | } 14 | argon: { 15 | config: ArgonConfig 16 | implementation: ArgonContract 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /env.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Validating Environment Variables 4 | |-------------------------------------------------------------------------- 5 | | 6 | | In this file we define the rules for validating environment variables. 7 | | By performing validation we ensure that your application is running in 8 | | a stable environment with correct configuration values. 9 | | 10 | | This file is read automatically by the framework during the boot lifecycle 11 | | and hence do not rename or move this file to a different location. 12 | | 13 | */ 14 | 15 | import Env from '@ioc:Adonis/Core/Env' 16 | 17 | export default Env.rules({ 18 | HOST: Env.schema.string({ format: 'host' }), 19 | PORT: Env.schema.number(), 20 | APP_KEY: Env.schema.string(), 21 | APP_NAME: Env.schema.string(), 22 | NODE_ENV: Env.schema.enum(['development', 'production', 'testing'] as const), 23 | }) 24 | -------------------------------------------------------------------------------- /lambda.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | import { Ignitor } from '@adonisjs/core/build/standalone' 3 | import Serverlessize from '@satheler/s12r' 4 | 5 | process.on('unhandledRejection', (reason) => { 6 | console.error(reason) 7 | }) 8 | 9 | process.on('uncaughtException', (reason) => { 10 | console.error(reason) 11 | }) 12 | 13 | let server: Function 14 | 15 | async function bootstrapServer() { 16 | const ignitor = new Ignitor(__dirname) 17 | const httpServer = ignitor.httpServer() 18 | 19 | await httpServer.application.setup() 20 | await httpServer.application.registerProviders() 21 | await httpServer.application.bootProviders() 22 | await httpServer.application.requirePreloads() 23 | 24 | 25 | const serverCore = httpServer.application.container.use('Adonis/Core/Server') 26 | serverCore.errorHandler('App/Exceptions/Handler') 27 | serverCore.optimize() 28 | 29 | 30 | const server = serverCore.handle.bind(serverCore) 31 | return server 32 | } 33 | 34 | export const handle = async (...args: any[]) => { 35 | if (!server) { 36 | server = await bootstrapServer() 37 | } 38 | 39 | const { request, response } = Serverlessize(args) 40 | return server(request, response) 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-adonis", 3 | "private": true, 4 | "version": "1.0.0", 5 | "main": "build/server.js", 6 | "author": "Gustavo Bittencourt Satheler ", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "node ace build --production", 10 | "start": "node server.js", 11 | "dev": "node ace serve --watch", 12 | "lint": "eslint . --ext=.ts", 13 | "serverless:start": "npm run build && sls offline -c serverless.aws.yml", 14 | "deploy": "rimraf .serverless && sls deploy", 15 | "deploy:aws": "rimraf tmp/layers && yarn layer:node_modules && yarn deploy -c serverless.aws.yml", 16 | "deploy:azure": "yarn deploy -c serverless.azure.yml", 17 | "layer:node_modules": "yarn install --modules-folder ./tmp/layers/nodejs/node_modules --production=true" 18 | }, 19 | "dependencies": { 20 | "@adonisjs/auth": "^8.0.7", 21 | "@adonisjs/core": "^5.1.9", 22 | "@adonisjs/lucid": "^15.0.1", 23 | "@adonisjs/repl": "^3.1.4", 24 | "@adonisjs/shield": "^7.0.4", 25 | "@satheler/s12r": "^0.2.7", 26 | "execa": "^5.0.0", 27 | "luxon": "^1.26.0", 28 | "pg": "^8.5.1", 29 | "phc-bcrypt": "^1.0.5", 30 | "proxy-addr": "^2.0.6", 31 | "reflect-metadata": "^0.1.13", 32 | "serverless-plugin-typescript": "^1.1.9", 33 | "source-map-support": "^0.5.19", 34 | "uuid": "^8.3.2" 35 | }, 36 | "devDependencies": { 37 | "@adonisjs/assembler": "^5.3.2", 38 | "@types/uuid": "^8.3.0", 39 | "adonis-preset-ts": "^2.0.0", 40 | "eslint": "^7.22.0", 41 | "eslint-plugin-adonis": "^1.2.1", 42 | "git-commit-msg-linter": "^3.0.3", 43 | "husky": "4", 44 | "lint-staged": "^10.5.4", 45 | "pino-pretty": "^4.7.1", 46 | "rimraf": "^3.0.2", 47 | "serverless": "^2.35.0", 48 | "serverless-domain-manager": "^5.1.0", 49 | "serverless-offline": "^6.9.0", 50 | "typescript": "~4.1", 51 | "youch": "^2.1.1", 52 | "youch-terminal": "^1.0.1" 53 | }, 54 | "engines": { 55 | "node": ">=12.x" 56 | }, 57 | "husky": { 58 | "hooks": { 59 | "pre-commit": "lint-staged" 60 | } 61 | }, 62 | "lint-staged": { 63 | "*.ts": [ 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /providers/AppProvider.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationContract } from '@ioc:Adonis/Core/Application' 2 | 3 | export default class AppProvider { 4 | constructor(protected app: ApplicationContract) {} 5 | 6 | public register() { 7 | // Register your own bindings 8 | } 9 | 10 | public async boot() { 11 | // IoC container is ready 12 | } 13 | 14 | public async ready() { 15 | // App is ready 16 | } 17 | 18 | public async shutdown() { 19 | // Cleanup, since app is going down 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | AdonisJs Server 4 | |-------------------------------------------------------------------------- 5 | | 6 | | The contents in this file is meant to bootstrap the AdonisJs application 7 | | and start the HTTP server to accept incoming connections. You must avoid 8 | | making this file dirty and instead make use of `lifecycle hooks` provided 9 | | by AdonisJs service providers for custom code. 10 | | 11 | */ 12 | 13 | import 'reflect-metadata' 14 | import sourceMapSupport from 'source-map-support' 15 | import { Ignitor } from '@adonisjs/core/build/standalone' 16 | 17 | sourceMapSupport.install({ handleUncaughtExceptions: false }) 18 | 19 | new Ignitor(__dirname).httpServer().start() 20 | -------------------------------------------------------------------------------- /serverless.aws.yml: -------------------------------------------------------------------------------- 1 | service: satheler-survey 2 | 3 | provider: 4 | name: aws 5 | region: ${opt:region, 'sa-east-1'} 6 | stage: ${opt:stage, 'develop'} 7 | runtime: nodejs14.x 8 | stackName: ${self:provider.stage}-${self:service} 9 | apiName: ${self:provider.stage}-${self:service} 10 | timeout: 10 11 | memorySize: 256 12 | versionFunctions: false 13 | tracing: 14 | apiGateway: true 15 | lambda: true 16 | environment: 17 | NODE_ENV: development 18 | PORT: 3333 19 | HOST: localhost 20 | APP_KEY: TVTj8OCKxhHhHWeyZHkVhK1TPL7ghIK9 21 | 22 | functions: 23 | app: 24 | handler: lambda.handle 25 | layers: 26 | - { Ref: NodeModulesLambdaLayer } 27 | events: 28 | - http: 29 | cors: true 30 | path: '/' 31 | method: any 32 | - http: 33 | cors: true 34 | path: '{proxy+}' 35 | method: any 36 | 37 | package: 38 | patterns: 39 | - 'build/**' 40 | - '!node_modules/**' 41 | - '!.github/**' 42 | - '!.vscode/**' 43 | - '!app/**' 44 | - '!commands/**' 45 | - '!config/**' 46 | - '!contracts/**' 47 | - '!database/**' 48 | - '!providers/**' 49 | - '!scripts/**' 50 | - '!start/**' 51 | - '!tests/**' 52 | - '!tmp/**' 53 | - '!./*' 54 | 55 | layers: 56 | NodeModules: 57 | name: ${self:provider.apiName}-layer 58 | path: tmp/layers 59 | description: "node_modules dependencies" 60 | 61 | plugins: 62 | - serverless-plugin-typescript 63 | - serverless-offline 64 | -------------------------------------------------------------------------------- /start/kernel.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Application middleware 4 | |-------------------------------------------------------------------------- 5 | | 6 | | This file is used to define middleware for HTTP requests. You can register 7 | | middleware as a `closure` or an IoC container binding. The bindings are 8 | | preferred, since they keep this file clean. 9 | | 10 | */ 11 | 12 | import Server from '@ioc:Adonis/Core/Server' 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | Global middleware 17 | |-------------------------------------------------------------------------- 18 | | 19 | | An array of global middleware, that will be executed in the order they 20 | | are defined for every HTTP requests. 21 | | 22 | */ 23 | Server.middleware.register([() => import('@ioc:Adonis/Core/BodyParser')]) 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Named middleware 28 | |-------------------------------------------------------------------------- 29 | | 30 | | Named middleware are defined as key-value pair. The value is the namespace 31 | | or middleware function and key is the alias. Later you can use these 32 | | alias on individual routes. For example: 33 | | 34 | | { auth: () => import('App/Middleware/Auth') } 35 | | 36 | | and then use it as follows 37 | | 38 | | Route.get('dashboard', 'UserController.dashboard').middleware('auth') 39 | | 40 | */ 41 | Server.middleware.registerNamed({}) 42 | -------------------------------------------------------------------------------- /start/routes.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Routes 4 | |-------------------------------------------------------------------------- 5 | | 6 | | This file is dedicated for defining HTTP routes. A single file is enough 7 | | for majority of projects, however you can define routes in different 8 | | files and just make sure to import them inside this file. For example 9 | | 10 | | Define routes in following two files 11 | | ├── start/routes/cart.ts 12 | | ├── start/routes/customer.ts 13 | | 14 | | and then import them inside `start/routes.ts` as follows 15 | | 16 | | import './routes/cart' 17 | | import './routes/customer' 18 | | 19 | */ 20 | 21 | import Route from '@ioc:Adonis/Core/Route' 22 | 23 | Route.get('/', async () => { 24 | return { hello: 'world' } 25 | }) 26 | 27 | Route.get('/test', async () => { 28 | return { hello: 'test' } 29 | }) 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "**/*", 4 | "./*" 5 | ], 6 | "exclude": [ 7 | "node_modules", 8 | "build" 9 | ], 10 | "compilerOptions": { 11 | "baseUrl": ".", 12 | "preserveConstEnums": true, 13 | "strictNullChecks": true, 14 | "target": "ESNext", 15 | "moduleResolution": "node", 16 | "module": "commonjs", 17 | "allowSyntheticDefaultImports": true, 18 | "emitDecoratorMetadata": true, 19 | "experimentalDecorators": true, 20 | "esModuleInterop": true, 21 | "noUnusedLocals": true, 22 | "skipLibCheck": true, 23 | "noUnusedParameters": true, 24 | "removeComments": true, 25 | "declaration": false, 26 | "lib": [ 27 | "ESNext" 28 | ], 29 | "outDir": ".build", 30 | "rootDir": "./", 31 | "sourceMap": true, 32 | "paths": { 33 | "App/*": [ 34 | "./app/*" 35 | ], 36 | "Config/*": [ 37 | "./config/*" 38 | ], 39 | "Contracts/*": [ 40 | "./contracts/*" 41 | ], 42 | "Database/*": [ 43 | "./database/*" 44 | ] 45 | }, 46 | "types": [ 47 | "@adonisjs/core", 48 | "@adonisjs/repl", 49 | "@adonisjs/lucid", 50 | "@adonisjs/auth" 51 | ] 52 | } 53 | } --------------------------------------------------------------------------------