create: new folder
This commit is contained in:
parent
ffa930b201
commit
4edeb880a6
45
.adonisrc.json
Normal file
45
.adonisrc.json
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"typescript": true,
|
||||||
|
"commands": [
|
||||||
|
"./commands",
|
||||||
|
"@adonisjs/core/commands",
|
||||||
|
"@adonisjs/repl/build/commands",
|
||||||
|
"@adonisjs/lucid/build/commands",
|
||||||
|
"@adonisjs/mail/build/commands"
|
||||||
|
],
|
||||||
|
"exceptionHandlerNamespace": "App/Exceptions/Handler",
|
||||||
|
"aliases": {
|
||||||
|
"App": "app",
|
||||||
|
"Config": "config",
|
||||||
|
"Database": "database",
|
||||||
|
"Contracts": "contracts"
|
||||||
|
},
|
||||||
|
"preloads": [
|
||||||
|
"./start/routes",
|
||||||
|
"./start/kernel"
|
||||||
|
],
|
||||||
|
"providers": [
|
||||||
|
"./providers/AppProvider",
|
||||||
|
"@adonisjs/core",
|
||||||
|
"@adonisjs/session",
|
||||||
|
"@adonisjs/view",
|
||||||
|
"@adonisjs/shield",
|
||||||
|
"@adonisjs/lucid",
|
||||||
|
"@adonisjs/auth",
|
||||||
|
"@adonisjs/mail",
|
||||||
|
"@adonisjs/ally"
|
||||||
|
],
|
||||||
|
"metaFiles": [
|
||||||
|
{
|
||||||
|
"pattern": "public/**",
|
||||||
|
"reloadServer": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "resources/views/**/*.edge",
|
||||||
|
"reloadServer": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aceProviders": [
|
||||||
|
"@adonisjs/repl"
|
||||||
|
]
|
||||||
|
}
|
||||||
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
insert_final_newline = ignore
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
21
.env.example
Normal file
21
.env.example
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
PORT=3333
|
||||||
|
HOST=0.0.0.0
|
||||||
|
NODE_ENV=development
|
||||||
|
APP_KEY=ltoWFqOgfC6B8eVs6Kj2YLxyyXCvwgcu
|
||||||
|
SESSION_DRIVER=cookie
|
||||||
|
CACHE_VIEWS=false
|
||||||
|
|
||||||
|
DB_CONNECTION=pg
|
||||||
|
PG_HOST=ec2-54-179-80-119.ap-southeast-1.compute.amazonaws.com
|
||||||
|
PG_PORT=5432
|
||||||
|
PG_USER=roadreportpgdb
|
||||||
|
PG_PASSWORD=roadreportpgdb1qaz
|
||||||
|
PG_DB_NAME=pg_roadreport_dev
|
||||||
|
|
||||||
|
SMTP_HOST=localhost
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USERNAME=<username>
|
||||||
|
SMTP_PASSWORD=<password>
|
||||||
|
|
||||||
|
GOOGLE_CLIENT_ID=clientId
|
||||||
|
GOOGLE_CLIENT_SECRET=clientSecret
|
||||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
coverage
|
||||||
|
.vscode
|
||||||
|
.DS_STORE
|
||||||
|
.env
|
||||||
|
tmp
|
||||||
|
package-lock.json
|
||||||
35
README.md
Normal file
35
README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# AdonisJS v5 Boilerplate
|
||||||
|
|
||||||
|
Using repository and service pattern
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Clone this repository `git clone git@gitlab.com:profile-image/boilerplate/adonisjs-v5-boilerplate.git`
|
||||||
|
2. Copy file `.env.example` to `.env`
|
||||||
|
3. Create database
|
||||||
|
4. Run `boilerplate.sql` in folder `database/sql`
|
||||||
|
5. Change database name and connection in `.env`
|
||||||
|
6. Run command `npm install` to install dependencies
|
||||||
|
7. Run server with `npm run dev`
|
||||||
|
|
||||||
|
## Creating Module
|
||||||
|
|
||||||
|
Create model, repository, service, controller, validators and route using below command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ace make:module <Namespace> <ModelName> --endpoint <EndpointName> --soft-delete --uuid
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: I will make module for user table with soft delete
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ace make:module User User --endpoint users --soft-delete
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
1. Namespace is required and using CamelCase.
|
||||||
|
2. ModelName is required and using CamelCase.
|
||||||
|
3. EndpointName is required and using lowercase. If endpoint have more than one word, separate them with `-`.
|
||||||
|
4. --soft-delete is optional. Use only when your table has `deleted_at` column.
|
||||||
|
5. --uuid is optional. Use only when your primaryKey using uuid.
|
||||||
16
ace
Normal file
16
ace
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Ace Commands
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This file is the entry point for running ace commands.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('reflect-metadata')
|
||||||
|
require('source-map-support').install({ handleUncaughtExceptions: false })
|
||||||
|
|
||||||
|
const { Ignitor } = require('@adonisjs/core/build/standalone')
|
||||||
|
new Ignitor(__dirname)
|
||||||
|
.ace()
|
||||||
|
.handle(process.argv.slice(2))
|
||||||
332
ace-manifest.json
Normal file
332
ace-manifest.json
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"make:module": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": false,
|
||||||
|
"stayAlive": false
|
||||||
|
},
|
||||||
|
"commandPath": "./commands/MakeModule",
|
||||||
|
"commandName": "make:module",
|
||||||
|
"description": "Make a new module",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"propertyName": "domain",
|
||||||
|
"name": "domain",
|
||||||
|
"required": true,
|
||||||
|
"description": "Domain name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"propertyName": "model",
|
||||||
|
"name": "model",
|
||||||
|
"required": true,
|
||||||
|
"description": "Model name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "soft-delete",
|
||||||
|
"propertyName": "softDelete",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable soft delete"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "enable-uuid",
|
||||||
|
"propertyName": "enableUUID",
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "uuid",
|
||||||
|
"description": "Enable soft delete"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "endpoint",
|
||||||
|
"propertyName": "endpoint",
|
||||||
|
"type": "string",
|
||||||
|
"alias": "e",
|
||||||
|
"description": "Set endpoint name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dump:rcfile": {
|
||||||
|
"settings": {},
|
||||||
|
"commandPath": "@adonisjs/core/commands/DumpRc",
|
||||||
|
"commandName": "dump:rcfile",
|
||||||
|
"description": "Dump contents of .adonisrc.json file along with defaults",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": []
|
||||||
|
},
|
||||||
|
"list:routes": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/core/commands/ListRoutes",
|
||||||
|
"commandName": "list:routes",
|
||||||
|
"description": "List application routes",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "json",
|
||||||
|
"propertyName": "json",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Output as JSON"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"generate:key": {
|
||||||
|
"settings": {},
|
||||||
|
"commandPath": "@adonisjs/core/commands/GenerateKey",
|
||||||
|
"commandName": "generate:key",
|
||||||
|
"description": "Generate a new APP_KEY secret",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": []
|
||||||
|
},
|
||||||
|
"repl": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true,
|
||||||
|
"environment": "repl",
|
||||||
|
"stayAlive": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/repl/build/commands/AdonisRepl",
|
||||||
|
"commandName": "repl",
|
||||||
|
"description": "Start a new REPL session",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": []
|
||||||
|
},
|
||||||
|
"db:seed": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/DbSeed",
|
||||||
|
"commandName": "db:seed",
|
||||||
|
"description": "Execute database seeder files",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "connection",
|
||||||
|
"propertyName": "connection",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Define a custom database connection for the seeders",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "interactive",
|
||||||
|
"propertyName": "interactive",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Run seeders in interactive mode",
|
||||||
|
"alias": "i"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "files",
|
||||||
|
"propertyName": "files",
|
||||||
|
"type": "array",
|
||||||
|
"description": "Define a custom set of seeders files names to run",
|
||||||
|
"alias": "f"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"make:model": {
|
||||||
|
"settings": {},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/MakeModel",
|
||||||
|
"commandName": "make:model",
|
||||||
|
"description": "Make a new Lucid model",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"propertyName": "name",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"description": "Name of the model class"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "migration",
|
||||||
|
"propertyName": "migration",
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "m",
|
||||||
|
"description": "Generate the migration for the model"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "controller",
|
||||||
|
"propertyName": "controller",
|
||||||
|
"type": "boolean",
|
||||||
|
"alias": "c",
|
||||||
|
"description": "Generate the controller for the model"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"make:migration": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/MakeMigration",
|
||||||
|
"commandName": "make:migration",
|
||||||
|
"description": "Make a new migration file",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"propertyName": "name",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"description": "Name of the migration file"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "connection",
|
||||||
|
"propertyName": "connection",
|
||||||
|
"type": "string",
|
||||||
|
"description": "The connection flag is used to lookup the directory for the migration file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "folder",
|
||||||
|
"propertyName": "folder",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Pre-select a migration directory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "create",
|
||||||
|
"propertyName": "create",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Define the table name for creating a new table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "table",
|
||||||
|
"propertyName": "table",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Define the table name for altering an existing table"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"make:seeder": {
|
||||||
|
"settings": {},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/MakeSeeder",
|
||||||
|
"commandName": "make:seeder",
|
||||||
|
"description": "Make a new Seeder file",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"propertyName": "name",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"description": "Name of the seeder class"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": []
|
||||||
|
},
|
||||||
|
"migration:run": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/Migration/Run",
|
||||||
|
"commandName": "migration:run",
|
||||||
|
"description": "Run pending migrations",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "connection",
|
||||||
|
"propertyName": "connection",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Define a custom database connection",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "force",
|
||||||
|
"propertyName": "force",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Explicitly force to run migrations in production"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dry-run",
|
||||||
|
"propertyName": "dryRun",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Print SQL queries, instead of running the migrations"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"migration:rollback": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/Migration/Rollback",
|
||||||
|
"commandName": "migration:rollback",
|
||||||
|
"description": "Rollback migrations to a given batch number",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "connection",
|
||||||
|
"propertyName": "connection",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Define a custom database connection",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "force",
|
||||||
|
"propertyName": "force",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Explictly force to run migrations in production"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dry-run",
|
||||||
|
"propertyName": "dryRun",
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Print SQL queries, instead of running the migrations"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "batch",
|
||||||
|
"propertyName": "batch",
|
||||||
|
"type": "number",
|
||||||
|
"description": "Define custom batch number for rollback. Use 0 to rollback to initial state"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"migration:status": {
|
||||||
|
"settings": {
|
||||||
|
"loadApp": true
|
||||||
|
},
|
||||||
|
"commandPath": "@adonisjs/lucid/build/commands/Migration/Status",
|
||||||
|
"commandName": "migration:status",
|
||||||
|
"description": "Check migrations current status.",
|
||||||
|
"args": [],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": [
|
||||||
|
{
|
||||||
|
"name": "connection",
|
||||||
|
"propertyName": "connection",
|
||||||
|
"type": "string",
|
||||||
|
"description": "Define a custom database connection",
|
||||||
|
"alias": "c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"make:mailer": {
|
||||||
|
"settings": {},
|
||||||
|
"commandPath": "@adonisjs/mail/build/commands/MakeMailer",
|
||||||
|
"commandName": "make:mailer",
|
||||||
|
"description": "Make a new mailer class",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"propertyName": "name",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"description": "Name of the mailer class"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliases": [],
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aliases": {}
|
||||||
|
}
|
||||||
381
app/Base/Repositories/BaseRepository.ts
Normal file
381
app/Base/Repositories/BaseRepository.ts
Normal file
|
|
@ -0,0 +1,381 @@
|
||||||
|
import { DateTime } from "luxon"
|
||||||
|
|
||||||
|
export default class BaseRepository {
|
||||||
|
protected model: any
|
||||||
|
protected mainModel: any
|
||||||
|
protected isSoftDelete: boolean
|
||||||
|
protected RELATIONS: string[]
|
||||||
|
protected RELATION_OPTIONS: any
|
||||||
|
|
||||||
|
constructor(model: any) {
|
||||||
|
this.model = model
|
||||||
|
this.mainModel = model
|
||||||
|
this.isSoftDelete = model.softDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll(pagination: any, sort: any, whereClauses: any, fields: any, search: any) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
this.model = this.model.query()
|
||||||
|
this.model = this.parseSelectedFields(this.model, fields)
|
||||||
|
this.model = this.parseWhere(this.model, whereClauses)
|
||||||
|
this.model = this.parseSearch(this.model, whereClauses, search)
|
||||||
|
this.model = this.parseRelation(this.model)
|
||||||
|
this.model = this.parseSort(this.model, sort)
|
||||||
|
if (pagination.page && pagination.limit) {
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
return await this.model.whereNull('deleted_at').paginate(pagination.page, pagination.limit)
|
||||||
|
}
|
||||||
|
return await this.model.paginate(pagination.page, pagination.limit)
|
||||||
|
} else {
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
return await this.model.whereNull('deleted_at')
|
||||||
|
}
|
||||||
|
return await this.model
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(data: any = {}) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
this.model = this.model.query()
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
this.model = this.model.whereNull('deleted_at')
|
||||||
|
}
|
||||||
|
if (data.sort) {
|
||||||
|
this.model = this.parseSort(this.model, data.sort)
|
||||||
|
}
|
||||||
|
return await this.model
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async store(data: any) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
return await this.model.create(data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async multiInsert(data: any[]) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
return await this.model.createMany(data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async show(id: any, fields: any) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
this.model = this.model.query().where(this.model.primaryKey, id)
|
||||||
|
this.model = this.parseSelectedFields(this.model, fields)
|
||||||
|
this.model = this.parseRelation(this.model)
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
this.model = this.model.whereNull('deleted_at')
|
||||||
|
}
|
||||||
|
return await this.model.first()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: any, data: any) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
if (! await this.model.find(id)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
data.updated_at = DateTime.now()
|
||||||
|
if (Object.keys(data).length) {
|
||||||
|
await this.model.query().where(this.model.primaryKey, id).update(data)
|
||||||
|
}
|
||||||
|
return await this.model.find(id)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id: any) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
const result = await this.model.find(id)
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
await this.model.query().where(this.model.primaryKey, id).update({ deleted_at: DateTime.local() })
|
||||||
|
} else {
|
||||||
|
await this.model.query().where(this.model.primaryKey, id).delete()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAll() {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
return await this.model.query().whereNull('deleted_at').update({ deleted_at: DateTime.local() })
|
||||||
|
} else {
|
||||||
|
return await this.model.query().delete()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async first() {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
this.model = this.model.query()
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
this.model = this.model.whereNull('deleted_at')
|
||||||
|
}
|
||||||
|
return await this.model.first()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async find(id: any) {
|
||||||
|
try {
|
||||||
|
this.model = this.mainModel
|
||||||
|
this.model = this.model.query().where(this.model.primaryKey, id)
|
||||||
|
if (this.isSoftDelete) {
|
||||||
|
this.model = this.model.whereNull('deleted_at')
|
||||||
|
}
|
||||||
|
return await this.model.first()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRelation(relation: string[]) {
|
||||||
|
this.RELATIONS = relation
|
||||||
|
}
|
||||||
|
|
||||||
|
setRelationOptions(relationOptions: any) {
|
||||||
|
this.RELATION_OPTIONS = relationOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
parseSelectedFields(model: any, fields: any) {
|
||||||
|
if (fields) {
|
||||||
|
model = model.select(fields)
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
parseWhere(model: any, whereClauses: any) {
|
||||||
|
if (whereClauses.data) {
|
||||||
|
if (whereClauses.operation == 'and') {
|
||||||
|
whereClauses.data.forEach((whereClause: any) => {
|
||||||
|
if (whereClause.operator == 'between') {
|
||||||
|
model = model.whereBetween(whereClause.attribute, whereClause.value)
|
||||||
|
} else {
|
||||||
|
if (whereClause.value == 'null') {
|
||||||
|
model = model.whereNull(whereClause.attribute)
|
||||||
|
} else {
|
||||||
|
if (whereClause.attribute.includes('.')) {
|
||||||
|
const attr = whereClause.attribute.split('.')
|
||||||
|
model = model.whereHas(attr[0], (builder: any) => {
|
||||||
|
builder.where(attr[1], whereClause.operator, whereClause.value)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
model = model.where(whereClause.attribute, whereClause.operator, whereClause.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
whereClauses.data.forEach((whereClause: any, index: number) => {
|
||||||
|
if (whereClause.operator == 'between') {
|
||||||
|
model = model.whereBetween(whereClause.attribute, whereClause.value)
|
||||||
|
} else {
|
||||||
|
if (index == 0) {
|
||||||
|
if (whereClause.value == 'null') {
|
||||||
|
model = model.whereNull(whereClause.attribute)
|
||||||
|
} else {
|
||||||
|
if (whereClause.attribute.includes('.')) {
|
||||||
|
const attr = whereClause.attribute.split('.')
|
||||||
|
model = model.whereHas(attr[0], (builder: any) => {
|
||||||
|
builder.where(attr[1], whereClause.operator, whereClause.value)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
model = model.where(whereClause.attribute, whereClause.operator, whereClause.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (whereClause.value == 'null') {
|
||||||
|
model = model.orWhereNull(whereClause.attribute)
|
||||||
|
} else {
|
||||||
|
if (whereClause.attribute.includes('.')) {
|
||||||
|
const attr = whereClause.attribute.split('.')
|
||||||
|
model = model.orWhereHas(attr[0], (builder: any) => {
|
||||||
|
builder.where(attr[1], whereClause.operator, whereClause.value)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
model = model.orWhere(whereClause.attribute, whereClause.operator, whereClause.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
parseRelation(model: any) {
|
||||||
|
if (this.RELATIONS) {
|
||||||
|
this.RELATIONS.forEach((relation) => {
|
||||||
|
if (relation.split('.').length > 1) {
|
||||||
|
const firstRelation = relation.substr(0, relation.indexOf('.'))
|
||||||
|
model = model.preload(firstRelation, (query) => {
|
||||||
|
if (this.RELATION_OPTIONS) {
|
||||||
|
let relationOption = this.RELATION_OPTIONS.find((item: any) => { return item.relation == firstRelation })
|
||||||
|
this.parseRelationOption(query, relationOption)
|
||||||
|
}
|
||||||
|
this.parseNestedRelation(query, relation.substr(relation.indexOf('.') + 1), firstRelation)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
model = model.preload(relation, (query) => {
|
||||||
|
if (this.RELATION_OPTIONS) {
|
||||||
|
let relationOption = this.RELATION_OPTIONS.find((item: any) => { return item.relation == relation })
|
||||||
|
this.parseRelationOption(query, relationOption)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
parseNestedRelation(query: any, relation: string, firstRelation: string) {
|
||||||
|
let relations = this.RELATIONS.filter(d => { return typeof d == 'string' })
|
||||||
|
relations = relations.filter(d => { return d.includes(firstRelation + '.') })
|
||||||
|
if (relations.length > 1) {
|
||||||
|
relations.map(data => {
|
||||||
|
this.parseNestedRelation(query, data.substr(data.indexOf('.') + 1), relation.substr(0, data.indexOf('.')))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (relation.indexOf('.') > 0) {
|
||||||
|
let subRelation = relation.substr(0, relation.indexOf('.'))
|
||||||
|
query.preload(subRelation, (subQuery) => {
|
||||||
|
if (this.RELATION_OPTIONS) {
|
||||||
|
let relationOption = this.RELATION_OPTIONS.find((item: any) => { return item.relation == subRelation })
|
||||||
|
this.parseRelationOption(subQuery, relationOption)
|
||||||
|
}
|
||||||
|
this.parseNestedRelation(subQuery, relation.substr(relation.indexOf('.') + 1), subRelation)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
query.preload(relation, (subQuery) => {
|
||||||
|
if (this.RELATION_OPTIONS) {
|
||||||
|
let relationOption = this.RELATION_OPTIONS.find((item: any) => { return item.relation == relation })
|
||||||
|
this.parseRelationOption(subQuery, relationOption)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseRelationOption(query: any, relationOption: any) {
|
||||||
|
if (relationOption) {
|
||||||
|
if (relationOption.fields) {
|
||||||
|
query = query.select(relationOption.fields)
|
||||||
|
}
|
||||||
|
if (relationOption.sort) {
|
||||||
|
query = query.orderBy(relationOption.sort, relationOption.order)
|
||||||
|
}
|
||||||
|
if (relationOption.filter) {
|
||||||
|
query = this.parseWhere(query, relationOption.filter)
|
||||||
|
}
|
||||||
|
if (relationOption.limit) {
|
||||||
|
query = query.limit(relationOption.limit)
|
||||||
|
}
|
||||||
|
if (relationOption.search) {
|
||||||
|
query = this.parseSearch(query, relationOption.filter, relationOption.search)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
parseSort(model: any, sort: any[]) {
|
||||||
|
if (sort) {
|
||||||
|
sort.forEach((sort: any) => {
|
||||||
|
model = model.orderBy(sort.attribute, sort.order)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
parseSearch(model: any, whereClauses: any, search: any) {
|
||||||
|
if (search) {
|
||||||
|
const data = search.data
|
||||||
|
const attributes = search.attributes
|
||||||
|
const operator = search.operator
|
||||||
|
if (attributes) {
|
||||||
|
model = model.where((query) => {
|
||||||
|
if (whereClauses.data.length > 0) {
|
||||||
|
attributes.forEach((attribute: string) => {
|
||||||
|
if (attribute.includes('.')) {
|
||||||
|
const attr = attribute.split('.')
|
||||||
|
const field = attr[attr.length - 1]
|
||||||
|
const relations = attr.slice(0, attr.length - 1)
|
||||||
|
query.whereHas(relations[0], (query: any) => {
|
||||||
|
this.parseNestedSearch(query, relations.slice(1), field, data, operator)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
query.orWhere(attribute, operator, data)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
attributes.forEach((attribute: any, index: number) => {
|
||||||
|
if (index == 0) {
|
||||||
|
if (attribute.includes('.')) {
|
||||||
|
const attr = attribute.split('.')
|
||||||
|
const field = attr[attr.length - 1]
|
||||||
|
const relations = attr.slice(0, attr.length - 1)
|
||||||
|
query.whereHas(relations[0], (query: any) => {
|
||||||
|
this.parseNestedSearch(query, relations.slice(1), field, data, operator)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
query.where(attribute, operator, data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (attribute.includes('.')) {
|
||||||
|
const attr = attribute.split('.')
|
||||||
|
const field = attr[attr.length - 1]
|
||||||
|
const relations = attr.slice(0, attr.length - 1)
|
||||||
|
query.orWhereHas(relations[0], (query: any) => {
|
||||||
|
this.parseNestedSearch(query, relations.slice(1), field, data, operator)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
query.orWhere(attribute, operator, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
parseNestedSearch(model: any, relations: any, field: any, value: any, operator: any = 'ilike') {
|
||||||
|
if (relations.length > 0) {
|
||||||
|
model = model.whereHas(relations[0], (query: any) => {
|
||||||
|
this.parseNestedSearch(query, relations.slice(1), field, value)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
model = model.where(field, operator, value)
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
}
|
||||||
127
app/Base/Services/BaseService.ts
Normal file
127
app/Base/Services/BaseService.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
export default class BaseService {
|
||||||
|
repository: any
|
||||||
|
|
||||||
|
constructor(repository: any) {
|
||||||
|
this.repository = repository
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll(options: any) {
|
||||||
|
try {
|
||||||
|
this.repository.setRelation(options.relation)
|
||||||
|
this.repository.setRelationOptions(options.relationOptions)
|
||||||
|
const results = await this.repository.getAll(options.pagination, options.sort, options.filter, options.fields, options.search)
|
||||||
|
return results
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async store(data: any) {
|
||||||
|
try {
|
||||||
|
return await this.repository.store(data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async show(id: any, options: any = {}) {
|
||||||
|
try {
|
||||||
|
this.repository.setRelation(options.relation)
|
||||||
|
this.repository.setRelationOptions(options.relationOptions)
|
||||||
|
return await this.repository.show(id, options.fields)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async find(id: any) {
|
||||||
|
try {
|
||||||
|
return await this.repository.find(id)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async first() {
|
||||||
|
try {
|
||||||
|
return await this.repository.first()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: any, data: any) {
|
||||||
|
try {
|
||||||
|
return await this.repository.update(id, data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id: any) {
|
||||||
|
try {
|
||||||
|
return await this.repository.delete(id)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAll() {
|
||||||
|
try {
|
||||||
|
return await this.repository.deleteAll()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDeletedAll(options: any) {
|
||||||
|
try {
|
||||||
|
this.repository.setRelation(options.relation)
|
||||||
|
const results = await this.repository.getDeletedAll(options.pagination, options.sort, options.filter, options.fields, options.search)
|
||||||
|
return results
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async showDeleted(id: any, options: any = {}) {
|
||||||
|
try {
|
||||||
|
this.repository.setRelation(options.relation)
|
||||||
|
return await this.repository.showDeleted(id, options.fields)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async restore(id: any) {
|
||||||
|
try {
|
||||||
|
return await this.repository.restore(id)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async restoreAll() {
|
||||||
|
try {
|
||||||
|
return await this.repository.restoreAll()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async permanentDelete(id: any) {
|
||||||
|
try {
|
||||||
|
return await this.repository.permanentDelete(id)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async permanentDeleteAll() {
|
||||||
|
try {
|
||||||
|
return await this.repository.permanentDeleteAll()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
215
app/Base/Services/ParseParamService.ts
Normal file
215
app/Base/Services/ParseParamService.ts
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
export default class ParseParamService {
|
||||||
|
LIKE = 'like';
|
||||||
|
EQUAL = 'eq';
|
||||||
|
NOT_EQUAL = 'ne';
|
||||||
|
GREATER_THAN = 'gt';
|
||||||
|
GREATER_THAN_EQUAL = 'gte';
|
||||||
|
LESS_THAN = 'lt';
|
||||||
|
LESS_THAN_EQUAL = 'lte';
|
||||||
|
BETWEEN = 'between';
|
||||||
|
|
||||||
|
OPERATOR_LIKE = 'ILIKE';
|
||||||
|
OPERATOR_EQUAL = '=';
|
||||||
|
OPERATOR_NOT_EQUAL = '!=';
|
||||||
|
OPERATOR_GREATER_THAN = '>';
|
||||||
|
OPERATOR_GREATER_THAN_EQUAL = '>=';
|
||||||
|
OPERATOR_LESS_THAN = '<';
|
||||||
|
OPERATOR_LESS_THAN_EQUAL = '<=';
|
||||||
|
|
||||||
|
OPERATION_DEFAULT = "and";
|
||||||
|
|
||||||
|
parse(data: { sort: any; fields: any; embed: any; search: any; page: any; limit: any; }) {
|
||||||
|
const paginateParams = this.parsePaginateParams(data)
|
||||||
|
const sortParams = this.parseSortParams(data.sort)
|
||||||
|
const filterParams = this.parseFilterParams(data)
|
||||||
|
const projectionParams = this.parseProjectionParams(data.fields)
|
||||||
|
const relationParams = this.parseRelationParams(data.embed)
|
||||||
|
const relationOptionParams = this.parseRelationOptionParams(data)
|
||||||
|
const searchParams = this.parseSearch(data.search)
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
pagination: paginateParams,
|
||||||
|
sort: sortParams,
|
||||||
|
filter: filterParams,
|
||||||
|
fields: projectionParams,
|
||||||
|
relation: relationParams,
|
||||||
|
search: searchParams,
|
||||||
|
relationOptions: relationOptionParams
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
parsePaginateParams(data: { page: any; limit: any; }) {
|
||||||
|
return {
|
||||||
|
page: data.page ?? null,
|
||||||
|
limit: data.limit ?? null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseSortParams(data: string) {
|
||||||
|
if (data) {
|
||||||
|
const sorts = data.split(',')
|
||||||
|
const parsedSort: any[] = []
|
||||||
|
|
||||||
|
sorts.forEach((sort: string) => {
|
||||||
|
if (sort.includes('-')) {
|
||||||
|
parsedSort.push({
|
||||||
|
order: 'desc',
|
||||||
|
attribute: sort.split('-')[1]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
parsedSort.push({
|
||||||
|
order: 'asc',
|
||||||
|
attribute: sort
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return parsedSort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseFilterParams(data: { [x: string]: { [x: string]: any; }; operation?: any; }) {
|
||||||
|
const filters: any[] = []
|
||||||
|
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
if (key != 'page' && key != 'limit' && key != 'sort' && key != 'fields' && key != 'embed' && key != 'operation' && key != 'search' && !key.includes('.')) {
|
||||||
|
Object.keys(data[key]).forEach(k => {
|
||||||
|
if (k == 'between') {
|
||||||
|
const values = data[key][k].split(',')
|
||||||
|
if (values.length != 2) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
filters.push({
|
||||||
|
attribute: key,
|
||||||
|
operator: this.BETWEEN,
|
||||||
|
value: values
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data[key][k].includes(',')) {
|
||||||
|
const values = data[key][k].split(',')
|
||||||
|
values.forEach((val: any) => {
|
||||||
|
filters.push(this.parseFilter(key, k, val))
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
filters.push(this.parseFilter(key, k, data[key][k]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
operation: data.operation || 'and',
|
||||||
|
data: filters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseProjectionParams(data: string) {
|
||||||
|
if (data) {
|
||||||
|
return data.split(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseRelationParams(data: string) {
|
||||||
|
if (data) {
|
||||||
|
return data.split(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseRelationOptionParams(data: { [x: string]: any; embed: any; }) {
|
||||||
|
const relationOptions: any[] = []
|
||||||
|
if (data.embed) {
|
||||||
|
data.embed.replace(/\./g, ',').split(',').forEach((relation: string) => {
|
||||||
|
const option: any = {}
|
||||||
|
option.relation = relation
|
||||||
|
|
||||||
|
if (data[`${relation}.fields`]) {
|
||||||
|
option.fields = data[`${relation}.fields`].split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[`${relation}.sort`]) {
|
||||||
|
option.sort = data[`${relation}.sort`].replace('-', '')
|
||||||
|
option.order = data[`${relation}.sort`].includes('-') ? 'desc' : 'asc'
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterParams = Object.keys(data).filter((key: string) => key.split('.')[0] == relation && key != `${relation}.operation` && key != `${relation}.limit` && key != `${relation}.search` && key != `${relation}.fields` && key != `${relation}.sort`).map(key => {
|
||||||
|
const filter: any = {}
|
||||||
|
const operator: any = Object.keys(data[key])[0]
|
||||||
|
filter.attribute = key.split('.')[1]
|
||||||
|
filter.operator = this.parseOperator(operator)
|
||||||
|
filter.value = data[key][operator]
|
||||||
|
return filter
|
||||||
|
})
|
||||||
|
|
||||||
|
if (filterParams.length > 0) {
|
||||||
|
option.filter = {
|
||||||
|
operation: data[`${relation}.operation`] || 'and',
|
||||||
|
data: filterParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[`${relation}.limit`]) {
|
||||||
|
option.limit = data[`${relation}.limit`]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[`${relation}.search`]) {
|
||||||
|
option.search = this.parseSearch(data[`${relation}.search`])
|
||||||
|
}
|
||||||
|
|
||||||
|
relationOptions.push(option)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return relationOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
parseSearch(data: { [x: string]: any; }) {
|
||||||
|
if (data) {
|
||||||
|
const search: any = {
|
||||||
|
data: null,
|
||||||
|
attributes: [],
|
||||||
|
operator: this.OPERATOR_LIKE
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
search.data = `%${data[key]}%`
|
||||||
|
search.attributes = key.split(',')
|
||||||
|
})
|
||||||
|
|
||||||
|
return search
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseFilter(attribute: string, operator: string, value: string) {
|
||||||
|
if (operator == this.LIKE) {
|
||||||
|
value = `%${value}%`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
attribute: attribute,
|
||||||
|
operator: this.parseOperator(operator),
|
||||||
|
value: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseOperator(operator: any) {
|
||||||
|
switch (operator) {
|
||||||
|
case this.LIKE:
|
||||||
|
return this.OPERATOR_LIKE
|
||||||
|
case this.EQUAL:
|
||||||
|
return this.OPERATOR_EQUAL
|
||||||
|
case this.NOT_EQUAL:
|
||||||
|
return this.OPERATOR_NOT_EQUAL
|
||||||
|
case this.GREATER_THAN:
|
||||||
|
return this.OPERATOR_GREATER_THAN
|
||||||
|
case this.GREATER_THAN_EQUAL:
|
||||||
|
return this.OPERATOR_GREATER_THAN_EQUAL
|
||||||
|
case this.LESS_THAN:
|
||||||
|
return this.OPERATOR_LESS_THAN
|
||||||
|
case this.LESS_THAN_EQUAL:
|
||||||
|
return this.OPERATOR_LESS_THAN_EQUAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
app/Base/Services/ParseUrlService.ts
Normal file
72
app/Base/Services/ParseUrlService.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
export default class ParseUrlService {
|
||||||
|
parseUrl(request: any, currentPage: any, lastPage: any) {
|
||||||
|
const url: any = {
|
||||||
|
nextUrl: null,
|
||||||
|
prevUrl: null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request) {
|
||||||
|
let urlToParsed: any = request.completeUrl(true)
|
||||||
|
url.nextUrl = this.parseNextUrl(urlToParsed, currentPage, lastPage)
|
||||||
|
url.prevUrl = this.parsePreviousUrl(urlToParsed, currentPage, lastPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
parseNextUrl(url: string, currentPage: number, lastPage: number) {
|
||||||
|
let nextUrl: any = null
|
||||||
|
|
||||||
|
if (currentPage < lastPage && currentPage >= 1) {
|
||||||
|
if (url.includes('page=')) {
|
||||||
|
const splitedUrl = url.split('page=')
|
||||||
|
if (splitedUrl[1].includes('&')) {
|
||||||
|
const lengthToRemove = splitedUrl[1].split('&')[0].length
|
||||||
|
nextUrl = splitedUrl[0] + 'page=' + (currentPage + 1) + splitedUrl[1].substring(lengthToRemove)
|
||||||
|
} else {
|
||||||
|
nextUrl = splitedUrl[0] + 'page=' + (currentPage + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (currentPage < 1) {
|
||||||
|
if (url.includes('page=')) {
|
||||||
|
const splitedUrl = url.split('page=')
|
||||||
|
if (splitedUrl[1].includes('&')) {
|
||||||
|
const lengthToRemove = splitedUrl[1].split('&')[0].length
|
||||||
|
nextUrl = splitedUrl[0] + 'page=' + 1 + splitedUrl[1].substring(lengthToRemove)
|
||||||
|
} else {
|
||||||
|
nextUrl = splitedUrl[0] + 'page=' + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
parsePreviousUrl(url: string, currentPage: number, lastPage: number) {
|
||||||
|
let previousUrl: any = null
|
||||||
|
|
||||||
|
if (currentPage <= lastPage && currentPage > 1) {
|
||||||
|
if (url.includes('page=')) {
|
||||||
|
const splitedUrl = url.split('page=')
|
||||||
|
if (splitedUrl[1].includes('&')) {
|
||||||
|
const lengthToRemove = splitedUrl[1].split('&')[0].length
|
||||||
|
previousUrl = splitedUrl[0] + 'page=' + (currentPage - 1) + splitedUrl[1].substring(lengthToRemove)
|
||||||
|
} else {
|
||||||
|
previousUrl = splitedUrl[0] + 'page=' + (currentPage - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (currentPage > lastPage && lastPage != 0) {
|
||||||
|
if (url.includes('page=')) {
|
||||||
|
const splitedUrl = url.split('page=')
|
||||||
|
if (splitedUrl[1].includes('&')) {
|
||||||
|
const lengthToRemove = splitedUrl[1].split('&')[0].length
|
||||||
|
previousUrl = splitedUrl[0] + 'page=' + lastPage + splitedUrl[1].substring(lengthToRemove)
|
||||||
|
} else {
|
||||||
|
previousUrl = splitedUrl[0] + 'page=' + lastPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return previousUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
83
app/Controllers/Http/Auth/AuthController.ts
Normal file
83
app/Controllers/Http/Auth/AuthController.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import AuthService from 'App/Services/Auth/AuthService'
|
||||||
|
import AccountService from 'App/Services/User/AccountService'
|
||||||
|
import Base64 from 'base-64'
|
||||||
|
|
||||||
|
export default class AuthController {
|
||||||
|
service = new AuthService()
|
||||||
|
accountService = new AccountService()
|
||||||
|
|
||||||
|
public async login ({ auth, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const credentials = this.getBasicAuth(request.header('authorization'))
|
||||||
|
const rememberMe = request.body().remember_me ?? false
|
||||||
|
const token = await this.service.login(credentials, auth, rememberMe)
|
||||||
|
return response.api(token, 'OK', 200, request)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async logout ({ auth, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await auth.use('api').revoke()
|
||||||
|
return response.api(null, 'Logout successful!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async oauthRedirect ({ ally, response }) {
|
||||||
|
try {
|
||||||
|
return ally.use('google').redirect()
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async oauthCallback ({ ally, auth, response }) {
|
||||||
|
try {
|
||||||
|
const google = await ally.use('google').user()
|
||||||
|
let user = await this.accountService.findByEmail(google.email)
|
||||||
|
if (!user) {
|
||||||
|
return response.error('akun tidak terdaftar')
|
||||||
|
}else{
|
||||||
|
if (!user.google_id) {
|
||||||
|
await this.accountService.update(user.id, {
|
||||||
|
google_id: google.id,
|
||||||
|
status:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await user.load('role')
|
||||||
|
const authResult = await this.service.generateToken(auth, user, true)
|
||||||
|
const encodeToken = Base64.encode(JSON.stringify(authResult))
|
||||||
|
return response.redirect().toPath(`http://localhost:4200/google-redirect?token=${encodeToken}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async forgotPassword ({ request, response }) {
|
||||||
|
try {
|
||||||
|
const data = await request.all()
|
||||||
|
await this.service.forgotPassword(data.credential, request)
|
||||||
|
return response.api(null, 'Forgot password link has been sent to your email')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restorePassword ({ request, response }) {
|
||||||
|
try {
|
||||||
|
const data = await request.all()
|
||||||
|
await this.service.restorePassword(data)
|
||||||
|
return response.api(null, 'Password restored!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getBasicAuth(authHeader: any) {
|
||||||
|
const data = Base64.decode(authHeader.split(' ')[1]).split(':')
|
||||||
|
return { email: data[0], password: data[1] }
|
||||||
|
}
|
||||||
|
}
|
||||||
96
app/Controllers/Http/User/AccountController.ts
Normal file
96
app/Controllers/Http/User/AccountController.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import AccountService from 'App/Services/User/AccountService'
|
||||||
|
import CreateAccountValidator from 'App/Validators/User/CreateAccountValidator'
|
||||||
|
import UpdateAccountValidator from 'App/Validators/User/UpdateAccountValidator'
|
||||||
|
import { ValidationException } from '@ioc:Adonis/Core/Validator'
|
||||||
|
|
||||||
|
export default class AccountController {
|
||||||
|
service = new AccountService()
|
||||||
|
FETCHED_ATTRIBUTE = [
|
||||||
|
'urole_id',
|
||||||
|
'username',
|
||||||
|
'pwd',
|
||||||
|
'email',
|
||||||
|
'google_id',
|
||||||
|
'fullname',
|
||||||
|
'avatar',
|
||||||
|
'is_ban',
|
||||||
|
]
|
||||||
|
|
||||||
|
public async index ({ request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const options = request.parseParams(request.all())
|
||||||
|
const result = await this.service.getAll(options)
|
||||||
|
return response.api(result, 'OK', 200, request)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async store ({ request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await request.validate(CreateAccountValidator)
|
||||||
|
const data = request.only(this.FETCHED_ATTRIBUTE)
|
||||||
|
const result = await this.service.store(data)
|
||||||
|
return response.api(result, 'Account created!', 201)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationException) {
|
||||||
|
const errorValidation: any = error
|
||||||
|
return response.error(errorValidation.message, errorValidation.messages.errors, 422)
|
||||||
|
}
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async show ({ params, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const options = request.parseParams(request.all())
|
||||||
|
const result = await this.service.show(params.id, options)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, `Account with id: ${params.id} not found`)
|
||||||
|
}
|
||||||
|
return response.api(result)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async update ({ params, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await request.validate(UpdateAccountValidator)
|
||||||
|
const data = request.only(this.FETCHED_ATTRIBUTE)
|
||||||
|
const result = await this.service.update(params.id, data)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, `Account with id: ${params.id} not found`)
|
||||||
|
}
|
||||||
|
return response.api(result, 'Account updated!')
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationException) {
|
||||||
|
const errorValidation: any = error
|
||||||
|
return response.error(errorValidation.message, errorValidation.messages.errors, 422)
|
||||||
|
}
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroy ({ params, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const result = await this.service.delete(params.id)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, `Account with id: ${params.id} not found`)
|
||||||
|
}
|
||||||
|
return response.api(null, 'Account deleted!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyAll ({ response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await this.service.deleteAll()
|
||||||
|
return response.api(null, 'All Account deleted!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
app/Controllers/Http/User/RoleController.ts
Normal file
90
app/Controllers/Http/User/RoleController.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import RoleService from 'App/Services/User/RoleService'
|
||||||
|
import CreateRoleValidator from 'App/Validators/User/CreateRoleValidator'
|
||||||
|
import UpdateRoleValidator from 'App/Validators/User/UpdateRoleValidator'
|
||||||
|
import { ValidationException } from '@ioc:Adonis/Core/Validator'
|
||||||
|
|
||||||
|
export default class RoleController {
|
||||||
|
service = new RoleService()
|
||||||
|
FETCHED_ATTRIBUTE = [
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
public async index ({ request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const options = request.parseParams(request.all())
|
||||||
|
const result = await this.service.getAll(options)
|
||||||
|
return response.api(result, 'OK', 200, request)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async store ({ request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await request.validate(CreateRoleValidator)
|
||||||
|
const data = request.only(this.FETCHED_ATTRIBUTE)
|
||||||
|
const result = await this.service.store(data)
|
||||||
|
return response.api(result, 'Role created!', 201)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationException) {
|
||||||
|
const errorValidation: any = error
|
||||||
|
return response.error(errorValidation.message, errorValidation.messages.errors, 422)
|
||||||
|
}
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async show ({ params, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const options = request.parseParams(request.all())
|
||||||
|
const result = await this.service.show(params.id, options)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, `Role with id: ${params.id} not found`)
|
||||||
|
}
|
||||||
|
return response.api(result)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async update ({ params, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await request.validate(UpdateRoleValidator)
|
||||||
|
const data = request.only(this.FETCHED_ATTRIBUTE)
|
||||||
|
const result = await this.service.update(params.id, data)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, `Role with id: ${params.id} not found`)
|
||||||
|
}
|
||||||
|
return response.api(result, 'Role updated!')
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationException) {
|
||||||
|
const errorValidation: any = error
|
||||||
|
return response.error(errorValidation.message, errorValidation.messages.errors, 422)
|
||||||
|
}
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroy ({ params, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const result = await this.service.delete(params.id)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, `Role with id: ${params.id} not found`)
|
||||||
|
}
|
||||||
|
return response.api(null, 'Role deleted!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyAll ({ response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await this.service.deleteAll()
|
||||||
|
return response.api(null, 'All Role deleted!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Exceptions/DefaultException.ts
Normal file
16
app/Exceptions/DefaultException.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Exception } from '@adonisjs/core/build/standalone'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Exception
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The Exception class imported from `@adonisjs/core` allows defining
|
||||||
|
| a status code and error code for every exception.
|
||||||
|
|
|
||||||
|
| @example
|
||||||
|
| new DefaultException('message', 500, 'E_RUNTIME_EXCEPTION')
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export default class DefaultException extends Exception {
|
||||||
|
}
|
||||||
45
app/Exceptions/Handler.ts
Normal file
45
app/Exceptions/Handler.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Http Exception Handler
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| AdonisJs will forward all exceptions occurred during an HTTP request to
|
||||||
|
| the following class. You can learn more about exception handling by
|
||||||
|
| reading docs.
|
||||||
|
|
|
||||||
|
| The exception handler extends a base `HttpExceptionHandler` which is not
|
||||||
|
| mandatory, however it can do lot of heavy lifting to handle the errors
|
||||||
|
| properly.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Logger from '@ioc:Adonis/Core/Logger'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import HttpExceptionHandler from '@ioc:Adonis/Core/HttpExceptionHandler'
|
||||||
|
|
||||||
|
export default class ExceptionHandler extends HttpExceptionHandler {
|
||||||
|
protected statusPages = {
|
||||||
|
'403': 'errors/unauthorized',
|
||||||
|
'404': 'errors/not-found',
|
||||||
|
'500..599': 'errors/server-error',
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
super(Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handle(error: any, ctx: HttpContextContract) {
|
||||||
|
/**
|
||||||
|
* Self handle the validation exception
|
||||||
|
*/
|
||||||
|
if (error.code === 'E_ROUTE_NOT_FOUND') {
|
||||||
|
return ctx.response.error(`Route [${ctx.request.method()}] ${ctx.request.completeUrl()} not found!`, null, 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forward rest of the exceptions to the parent class
|
||||||
|
*/
|
||||||
|
return super.handle(error, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
76
app/Middleware/Auth.ts
Normal file
76
app/Middleware/Auth.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { GuardsList } from '@ioc:Adonis/Addons/Auth'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import { AuthenticationException } from '@adonisjs/auth/build/standalone'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth middleware is meant to restrict un-authenticated access to a given route
|
||||||
|
* or a group of routes.
|
||||||
|
*
|
||||||
|
* You must register this middleware inside `start/kernel.ts` file under the list
|
||||||
|
* of named middleware.
|
||||||
|
*/
|
||||||
|
export default class AuthMiddleware {
|
||||||
|
/**
|
||||||
|
* The URL to redirect to when request is Unauthorized
|
||||||
|
*/
|
||||||
|
protected redirectTo = '/login'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates the current HTTP request against a custom set of defined
|
||||||
|
* guards.
|
||||||
|
*
|
||||||
|
* The authentication loop stops as soon as the user is authenticated using any
|
||||||
|
* of the mentioned guards and that guard will be used by the rest of the code
|
||||||
|
* during the current request.
|
||||||
|
*/
|
||||||
|
protected async authenticate(auth: HttpContextContract['auth'], guards: (keyof GuardsList)[]) {
|
||||||
|
/**
|
||||||
|
* Hold reference to the guard last attempted within the for loop. We pass
|
||||||
|
* the reference of the guard to the "AuthenticationException", so that
|
||||||
|
* it can decide the correct response behavior based upon the guard
|
||||||
|
* driver
|
||||||
|
*/
|
||||||
|
let guardLastAttempted: string | undefined
|
||||||
|
|
||||||
|
for (let guard of guards) {
|
||||||
|
guardLastAttempted = guard
|
||||||
|
|
||||||
|
if (await auth.use(guard).check()) {
|
||||||
|
/**
|
||||||
|
* Instruct auth to use the given guard as the default guard for
|
||||||
|
* the rest of the request, since the user authenticated
|
||||||
|
* succeeded here
|
||||||
|
*/
|
||||||
|
auth.defaultGuard = guard
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unable to authenticate using any guard
|
||||||
|
*/
|
||||||
|
throw new AuthenticationException(
|
||||||
|
'Unauthorized access',
|
||||||
|
'E_UNAUTHORIZED_ACCESS',
|
||||||
|
guardLastAttempted,
|
||||||
|
this.redirectTo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle request
|
||||||
|
*/
|
||||||
|
public async handle (
|
||||||
|
{ auth }: HttpContextContract,
|
||||||
|
next: () => Promise<void>,
|
||||||
|
customGuards: (keyof GuardsList)[]
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Uses the user defined guards or the default guard mentioned in
|
||||||
|
* the config file
|
||||||
|
*/
|
||||||
|
const guards = customGuards.length ? customGuards : [auth.name]
|
||||||
|
await this.authenticate(auth, guards)
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Middleware/SilentAuth.ts
Normal file
21
app/Middleware/SilentAuth.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Silent auth middleware can be used as a global middleware to silent check
|
||||||
|
* if the user is logged-in or not.
|
||||||
|
*
|
||||||
|
* The request continues as usual, even when the user is not logged-in.
|
||||||
|
*/
|
||||||
|
export default class SilentAuthMiddleware {
|
||||||
|
/**
|
||||||
|
* Handle request
|
||||||
|
*/
|
||||||
|
public async handle({ auth }: HttpContextContract, next: () => Promise<void>) {
|
||||||
|
/**
|
||||||
|
* Check if user is logged-in or not. If yes, then `ctx.auth.user` will be
|
||||||
|
* set to the instance of the currently logged in user.
|
||||||
|
*/
|
||||||
|
await auth.check()
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
}
|
||||||
62
app/Models/User/Account.ts
Normal file
62
app/Models/User/Account.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
import { BaseModel, beforeFetch, beforeFind, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm'
|
||||||
|
import Role from './Role'
|
||||||
|
|
||||||
|
export default class Account extends BaseModel {
|
||||||
|
public static softDelete = true
|
||||||
|
|
||||||
|
@column({ isPrimary: true })
|
||||||
|
public id: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public urole_id: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public username: string
|
||||||
|
|
||||||
|
@column({ serializeAs: null })
|
||||||
|
public pwd: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public email: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public google_id: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public fullname: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public avatar: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public is_ban: boolean
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true })
|
||||||
|
public created_at: DateTime
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
||||||
|
public updated_at: DateTime
|
||||||
|
|
||||||
|
@column.dateTime()
|
||||||
|
public deleted_at: DateTime
|
||||||
|
|
||||||
|
static get table() {
|
||||||
|
return "user.account"
|
||||||
|
}
|
||||||
|
|
||||||
|
@beforeFind()
|
||||||
|
public static findWithoutSoftDeletes(query) {
|
||||||
|
query.whereNull("deleted_at")
|
||||||
|
}
|
||||||
|
|
||||||
|
@beforeFetch()
|
||||||
|
public static fetchWithoutSoftDeletes(query) {
|
||||||
|
query.whereNull("deleted_at")
|
||||||
|
}
|
||||||
|
|
||||||
|
@belongsTo(() => Role, {
|
||||||
|
foreignKey: 'urole_id'
|
||||||
|
})
|
||||||
|
public role: BelongsTo<typeof Role>
|
||||||
|
}
|
||||||
23
app/Models/User/Role.ts
Normal file
23
app/Models/User/Role.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
|
||||||
|
|
||||||
|
export default class Role extends BaseModel {
|
||||||
|
@column({ isPrimary: true })
|
||||||
|
public id: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public code: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public name: string
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true })
|
||||||
|
public created_at: DateTime
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
||||||
|
public updated_at: DateTime
|
||||||
|
|
||||||
|
static get table() {
|
||||||
|
return "user.role"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Repositories/User/AccountRepository.ts
Normal file
17
app/Repositories/User/AccountRepository.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BaseRepository from "App/Base/Repositories/BaseRepository";
|
||||||
|
import Account from "App/Models/User/Account";
|
||||||
|
|
||||||
|
export default class AccountRepository extends BaseRepository {
|
||||||
|
constructor() {
|
||||||
|
super(Account)
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByEmail(email: string) {
|
||||||
|
try {
|
||||||
|
return await this.model.query().where('email', email).first()
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
9
app/Repositories/User/RoleRepository.ts
Normal file
9
app/Repositories/User/RoleRepository.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import BaseRepository from "App/Base/Repositories/BaseRepository";
|
||||||
|
import Role from "App/Models/User/Role";
|
||||||
|
|
||||||
|
export default class RoleRepository extends BaseRepository {
|
||||||
|
constructor() {
|
||||||
|
super(Role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
113
app/Services/Auth/AuthService.ts
Normal file
113
app/Services/Auth/AuthService.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import AccountRepository from "App/Repositories/User/AccountRepository"
|
||||||
|
import Hash from "@ioc:Adonis/Core/Hash"
|
||||||
|
import * as jwt from 'jsonwebtoken'
|
||||||
|
import DefaultException from "App/Exceptions/DefaultException"
|
||||||
|
import moment from 'moment'
|
||||||
|
import Base64 from 'base-64'
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import Mail from "@ioc:Adonis/Addons/Mail"
|
||||||
|
|
||||||
|
export default class AuthService {
|
||||||
|
accountRepository = new AccountRepository()
|
||||||
|
|
||||||
|
async login (credentials: any, auth: any, rememberMe: boolean) {
|
||||||
|
try {
|
||||||
|
const user = await this.accountRepository.findByEmail(credentials.email)
|
||||||
|
if (!user || !(await Hash.verify(user.pwd, credentials.password))) {
|
||||||
|
throw new DefaultException('Invalid email or password!')
|
||||||
|
}
|
||||||
|
if (user.is_ban) {
|
||||||
|
throw new DefaultException('User banned!')
|
||||||
|
}
|
||||||
|
await user.load("role");
|
||||||
|
return await this.generateToken(auth, user, rememberMe)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async forgotPassword (credential, request) {
|
||||||
|
try {
|
||||||
|
const user = await this.accountRepository.findByEmail(credential)
|
||||||
|
if (!user) {
|
||||||
|
throw new DefaultException('User not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await this.generateRestorePasswordToken(user)
|
||||||
|
const restoreUrlWithToken = request.header('origin') + 'reset-password?token=' + token
|
||||||
|
|
||||||
|
await this.sendEmailRestorePassword(user.email, restoreUrlWithToken)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async restorePassword (request) {
|
||||||
|
try {
|
||||||
|
const { expiresIn, credential } = await this.decryptToken(request.token)
|
||||||
|
if (!this.checkExpirationToken(expiresIn)) {
|
||||||
|
throw new DefaultException('Token expired')
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.accountRepository.findByEmail(credential)
|
||||||
|
await this.accountRepository.update(user.id, {
|
||||||
|
pwd: await Hash.make(request.pwd)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateToken(auth: any, user: any, rememberMe: boolean) {
|
||||||
|
try {
|
||||||
|
const expiresIn = rememberMe ? '7days' : '4hours'
|
||||||
|
const token = await auth.use('api').generate(user, {expiresIn})
|
||||||
|
const jwtToken = jwt.sign({user}, 'PeSgVkYp3s6v9y$B&E)H@McQfTjWnZq4', {expiresIn: rememberMe ? '7d' : '4h'})
|
||||||
|
return { token: token.token, jwtToken }
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateRestorePasswordToken (user) {
|
||||||
|
try {
|
||||||
|
const expiresIn = moment().add(30, 'm')
|
||||||
|
const token = Base64.encode(expiresIn + ';' + user.email)
|
||||||
|
return token
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async decryptToken (token) {
|
||||||
|
try {
|
||||||
|
const encrypt = Base64.decode(token).split(';')
|
||||||
|
const result = {
|
||||||
|
expiresIn: encrypt[0],
|
||||||
|
credential: encrypt[1]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendEmailRestorePassword (email, token) {
|
||||||
|
try {
|
||||||
|
await Mail.send((message) => {
|
||||||
|
message
|
||||||
|
.from(Env.get('SMTP_USERNAME'), 'Me')
|
||||||
|
.to(email)
|
||||||
|
.subject('Restore Password!')
|
||||||
|
.htmlView('emails/restore_password', { token })
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkExpirationToken (expiresIn) {
|
||||||
|
return expiresIn > moment()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
app/Services/User/AccountService.ts
Normal file
41
app/Services/User/AccountService.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import BaseService from "App/Base/Services/BaseService"
|
||||||
|
import AccountRepository from "App/Repositories/User/AccountRepository"
|
||||||
|
import Hash from '@ioc:Adonis/Core/Hash'
|
||||||
|
|
||||||
|
export default class AccountService extends BaseService {
|
||||||
|
constructor() {
|
||||||
|
super(new AccountRepository())
|
||||||
|
}
|
||||||
|
|
||||||
|
async store(data: any) {
|
||||||
|
try {
|
||||||
|
if (data.pwd) {
|
||||||
|
data.pwd = await Hash.make(data.pwd)
|
||||||
|
}
|
||||||
|
return await this.repository.store(data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: any, data: any) {
|
||||||
|
try {
|
||||||
|
if (data.pwd) {
|
||||||
|
data.pwd = await Hash.make(data.pwd)
|
||||||
|
}
|
||||||
|
return await this.repository.update(id, data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByEmail (email: string) {
|
||||||
|
try {
|
||||||
|
const akun = await this.repository.findByEmail(email)
|
||||||
|
return akun
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
9
app/Services/User/RoleService.ts
Normal file
9
app/Services/User/RoleService.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import BaseService from "App/Base/Services/BaseService"
|
||||||
|
import RoleRepository from "App/Repositories/User/RoleRepository"
|
||||||
|
|
||||||
|
export default class RoleService extends BaseService {
|
||||||
|
constructor() {
|
||||||
|
super(new RoleRepository())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
37
app/Validators/User/CreateAccountValidator.ts
Normal file
37
app/Validators/User/CreateAccountValidator.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { schema, validator, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import Account from 'App/Models/User/Account'
|
||||||
|
import Role from 'App/Models/User/Role'
|
||||||
|
|
||||||
|
export default class CreateAccountValidator {
|
||||||
|
constructor (protected ctx: HttpContextContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public reporter = validator.reporters.api
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
urole_id: schema.string({}, [
|
||||||
|
rules.exists({table: Role.table, column: Role.primaryKey})
|
||||||
|
]),
|
||||||
|
username: schema.string({}, [
|
||||||
|
rules.maxLength(100),
|
||||||
|
rules.unique({table: Account.table, column: 'username', where: {deleted_at: null}})
|
||||||
|
]),
|
||||||
|
pwd: schema.string({}, [
|
||||||
|
rules.minLength(6)
|
||||||
|
]),
|
||||||
|
email: schema.string({}, [
|
||||||
|
rules.maxLength(255),
|
||||||
|
rules.email(),
|
||||||
|
rules.unique({column: 'email', table: Account.table, where: {deleted_at: null}})
|
||||||
|
]),
|
||||||
|
google_id: schema.string.optional({}, [
|
||||||
|
rules.maxLength(255)
|
||||||
|
]),
|
||||||
|
fullname: schema.string({}, [
|
||||||
|
rules.maxLength(100)
|
||||||
|
]),
|
||||||
|
avatar: schema.string.optional(),
|
||||||
|
is_ban: schema.boolean.optional(),
|
||||||
|
})
|
||||||
|
}
|
||||||
18
app/Validators/User/CreateRoleValidator.ts
Normal file
18
app/Validators/User/CreateRoleValidator.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { schema, validator, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
export default class CreateRoleValidator {
|
||||||
|
constructor (protected ctx: HttpContextContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public reporter = validator.reporters.api
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
code: schema.string({}, [
|
||||||
|
rules.maxLength(4)
|
||||||
|
]),
|
||||||
|
name: schema.string({}, [
|
||||||
|
rules.maxLength(50)
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
}
|
||||||
37
app/Validators/User/UpdateAccountValidator.ts
Normal file
37
app/Validators/User/UpdateAccountValidator.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { schema, validator, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import Role from 'App/Models/User/Role'
|
||||||
|
import Account from 'App/Models/User/Account'
|
||||||
|
|
||||||
|
export default class UpdateAccountValidator {
|
||||||
|
constructor (protected ctx: HttpContextContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public reporter = validator.reporters.api
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
urole_id: schema.string.optional({}, [
|
||||||
|
rules.exists({table: Role.table, column: Role.primaryKey})
|
||||||
|
]),
|
||||||
|
username: schema.string.optional({}, [
|
||||||
|
rules.maxLength(100),
|
||||||
|
rules.unique({table: Account.table, column: 'username', where: {deleted_at: null}, whereNot: {id: this.ctx.params.id}})
|
||||||
|
]),
|
||||||
|
pwd: schema.string.optional({}, [
|
||||||
|
rules.minLength(6)
|
||||||
|
]),
|
||||||
|
email: schema.string.optional({}, [
|
||||||
|
rules.maxLength(255),
|
||||||
|
rules.email(),
|
||||||
|
rules.unique({column: 'email', table: Account.table, where: {deleted_at: null}, whereNot: {id: this.ctx.params.id}})
|
||||||
|
]),
|
||||||
|
google_id: schema.string.optional({}, [
|
||||||
|
rules.maxLength(255)
|
||||||
|
]),
|
||||||
|
fullname: schema.string.optional({}, [
|
||||||
|
rules.maxLength(100)
|
||||||
|
]),
|
||||||
|
avatar: schema.string.optional(),
|
||||||
|
is_ban: schema.boolean.optional(),
|
||||||
|
})
|
||||||
|
}
|
||||||
18
app/Validators/User/UpdateRoleValidator.ts
Normal file
18
app/Validators/User/UpdateRoleValidator.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { schema, validator, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
export default class UpdateRoleValidator {
|
||||||
|
constructor (protected ctx: HttpContextContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public reporter = validator.reporters.api
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
code: schema.string.optional({}, [
|
||||||
|
rules.maxLength(4)
|
||||||
|
]),
|
||||||
|
name: schema.string.optional({}, [
|
||||||
|
rules.maxLength(50)
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
}
|
||||||
270
commands/MakeModule.ts
Normal file
270
commands/MakeModule.ts
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
import { BaseCommand, args, flags } from '@adonisjs/core/build/standalone'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
export default class MakeModule extends BaseCommand {
|
||||||
|
public static commandName = 'make:module'
|
||||||
|
public static description = 'Make a new module'
|
||||||
|
|
||||||
|
@args.string({ description: "Domain name" })
|
||||||
|
public domain: string
|
||||||
|
|
||||||
|
@args.string({ description: "Model name" })
|
||||||
|
public model: string
|
||||||
|
|
||||||
|
@flags.boolean({ description: "Enable soft delete" })
|
||||||
|
public softDelete: boolean
|
||||||
|
|
||||||
|
@flags.boolean({ alias: 'uuid', description: "Enable soft delete" })
|
||||||
|
public enableUUID: boolean
|
||||||
|
|
||||||
|
@flags.string({ alias: 'e', description: "Set endpoint name"})
|
||||||
|
public endpoint: string
|
||||||
|
|
||||||
|
public static settings = {
|
||||||
|
loadApp: false,
|
||||||
|
stayAlive: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
if (!fs.existsSync(`app/Models/${this.domain}`)) {
|
||||||
|
if (!fs.existsSync('app/Models')) {
|
||||||
|
fs.mkdirSync(`app/Models`)
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`app/Models/${this.domain}`)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`app/Repositories/${this.domain}`)) {
|
||||||
|
if (!fs.existsSync('app/Repositories')) {
|
||||||
|
fs.mkdirSync(`app/Repositories`)
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`app/Repositories/${this.domain}`)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`app/Services/${this.domain}`)) {
|
||||||
|
if (!fs.existsSync('app/Services')) {
|
||||||
|
fs.mkdirSync(`app/Services`)
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`app/Services/${this.domain}`)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`app/Controllers/Http/${this.domain}`)) {
|
||||||
|
if (!fs.existsSync('app/Controllers')) {
|
||||||
|
fs.mkdirSync(`app/Controllers`)
|
||||||
|
fs.mkdirSync(`app/Controllers/Http`)
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`app/Controllers/Http/${this.domain}`)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`app/Validators/${this.domain}`)) {
|
||||||
|
if (!fs.existsSync('app/Validators')) {
|
||||||
|
fs.mkdirSync(`app/Validators`)
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`app/Validators/${this.domain}`)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(`start/routes/${this.domain.toLowerCase()}`)) {
|
||||||
|
if (!fs.existsSync('start/routes')) {
|
||||||
|
fs.mkdirSync(`start/routes`)
|
||||||
|
}
|
||||||
|
fs.mkdirSync(`start/routes/${this.domain.toLowerCase()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(`app/Models/${this.domain}/${this.model}.ts`, this.generateModel())
|
||||||
|
this.logger.success(`${this.model} Model Created!`)
|
||||||
|
|
||||||
|
fs.writeFileSync(`app/Repositories/${this.domain}/${this.model}Repository.ts`, this.generateRepository())
|
||||||
|
this.logger.success(`${this.model} Repository Created!`)
|
||||||
|
|
||||||
|
fs.writeFileSync(`app/Services/${this.domain}/${this.model}Service.ts`, this.generateService())
|
||||||
|
this.logger.success(`${this.model} Service Created!`)
|
||||||
|
|
||||||
|
fs.writeFileSync(`app/Controllers/Http/${this.domain}/${this.model}Controller.ts`, this.generateController())
|
||||||
|
this.logger.success(`${this.model} Controller Created!`)
|
||||||
|
|
||||||
|
fs.writeFileSync(`app/Validators/${this.domain}/Create${this.model}Validator.ts`, this.generateCreateValidator())
|
||||||
|
this.logger.success(`${this.model} Create Validator Created!`)
|
||||||
|
|
||||||
|
fs.writeFileSync(`app/Validators/${this.domain}/Update${this.model}Validator.ts`, this.generateUpdateValidator())
|
||||||
|
this.logger.success(`${this.model} Update Validator Created!`)
|
||||||
|
|
||||||
|
fs.writeFileSync(`start/routes/${this.domain.toLowerCase()}/${this.endpoint}.ts`, this.generateRoute())
|
||||||
|
this.logger.success(`${this.model} Route Created!`)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateModel() {
|
||||||
|
return `import { DateTime } from 'luxon'
|
||||||
|
import { BaseModel${this.enableUUID ? ', beforeCreate' : ''}${this.softDelete ? ', beforeFetch, beforeFind' : ''}, column } from '@ioc:Adonis/Lucid/Orm'${this.enableUUID ? "\nimport { v4 as uuidv4, v5 as uuidv5 } from 'uuid'" : ''}
|
||||||
|
|
||||||
|
export default class ${this.model} extends BaseModel {
|
||||||
|
${this.softDelete ? 'public static softDelete = true\n\n ' : ''}@column({ isPrimary: true })
|
||||||
|
public id: string
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true })
|
||||||
|
public created_at: DateTime
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
||||||
|
public updated_at: DateTime
|
||||||
|
|
||||||
|
${this.softDelete ? '@column.dateTime()\n public deleted_at: DateTime\n\n ' : ''}static get table() {
|
||||||
|
return "" // table name
|
||||||
|
}${this.softDelete ? '\n\n @beforeFind()\n public static findWithoutSoftDeletes(query) {\n query.whereNull("deleted_at")\n }\n\n @beforeFetch()\n public static fetchWithoutSoftDeletes(query) {\n query.whereNull("deleted_at")\n }' : ''}${this.enableUUID ? "\n\n @beforeCreate()\n public static setUUID(data: " + this.model + ") {\n const namespace = uuidv4()\n data.id = uuidv5('" + this.model + "', namespace)\n }" : ''}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateRepository() {
|
||||||
|
return `import BaseRepository from "App/Base/Repositories/BaseRepository";
|
||||||
|
import ${this.model} from "App/Models/${this.domain}/${this.model}";
|
||||||
|
|
||||||
|
export default class ${this.model}Repository extends BaseRepository {
|
||||||
|
constructor() {
|
||||||
|
super(${this.model})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateService() {
|
||||||
|
return `import BaseService from "App/Base/Services/BaseService"
|
||||||
|
import ${this.model}Repository from "App/Repositories/${this.domain}/${this.model}Repository"
|
||||||
|
|
||||||
|
export default class ${this.model}Service extends BaseService {
|
||||||
|
constructor() {
|
||||||
|
super(new ${this.model}Repository())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateController() {
|
||||||
|
return `import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import ${this.model}Service from 'App/Services/${this.domain}/${this.model}Service'
|
||||||
|
import Create${this.model}Validator from 'App/Validators/${this.domain}/Create${this.model}Validator'
|
||||||
|
import Update${this.model}Validator from 'App/Validators/${this.domain}/Update${this.model}Validator'
|
||||||
|
import { ValidationException } from '@ioc:Adonis/Core/Validator'
|
||||||
|
|
||||||
|
export default class ${this.model}Controller {
|
||||||
|
service = new ${this.model}Service()
|
||||||
|
FETCHED_ATTRIBUTE = [
|
||||||
|
// attribute
|
||||||
|
]
|
||||||
|
|
||||||
|
public async index ({ request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const options = request.parseParams(request.all())
|
||||||
|
const result = await this.service.getAll(options)
|
||||||
|
return response.api(result, 'OK', 200, request)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async store ({ request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await request.validate(Create${this.model}Validator)
|
||||||
|
const data = request.only(this.FETCHED_ATTRIBUTE)
|
||||||
|
const result = await this.service.store(data)
|
||||||
|
return response.api(result, '${this.model} created!', 201)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationException) {
|
||||||
|
const errorValidation: any = error
|
||||||
|
return response.error(errorValidation.message, errorValidation.messages.errors, 422)
|
||||||
|
}
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async show ({ params, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const options = request.parseParams(request.all())
|
||||||
|
const result = await this.service.show(params.id, options)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, ${'`'+ this.model + ' with id: ${params.id} not found`'})
|
||||||
|
}
|
||||||
|
return response.api(result)
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async update ({ params, request, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await request.validate(Update${this.model}Validator)
|
||||||
|
const data = request.only(this.FETCHED_ATTRIBUTE)
|
||||||
|
const result = await this.service.update(params.id, data)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, ${'`'+ this.model + ' with id: ${params.id} not found`'})
|
||||||
|
}
|
||||||
|
return response.api(result, '${this.model} updated!')
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationException) {
|
||||||
|
const errorValidation: any = error
|
||||||
|
return response.error(errorValidation.message, errorValidation.messages.errors, 422)
|
||||||
|
}
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroy ({ params, response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
const result = await this.service.delete(params.id)
|
||||||
|
if (!result) {
|
||||||
|
return response.api(null, ${'`'+ this.model + ' with id: ${params.id} not found`'})
|
||||||
|
}
|
||||||
|
return response.api(null, '${this.model} deleted!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyAll ({ response }: HttpContextContract) {
|
||||||
|
try {
|
||||||
|
await this.service.deleteAll()
|
||||||
|
return response.api(null, 'All ${this.model} deleted!')
|
||||||
|
} catch (error) {
|
||||||
|
return response.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCreateValidator() {
|
||||||
|
return `import { schema, validator, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
export default class Create${this.model}Validator {
|
||||||
|
constructor (protected ctx: HttpContextContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public reporter = validator.reporters.api
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
// your validation rules
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateUpdateValidator() {
|
||||||
|
return `import { schema, validator, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
export default class Update${this.model}Validator {
|
||||||
|
constructor (protected ctx: HttpContextContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public reporter = validator.reporters.api
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
// your validation rules
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateRoute() {
|
||||||
|
return `import Route from '@ioc:Adonis/Core/Route'
|
||||||
|
|
||||||
|
Route.group(function () {
|
||||||
|
Route.delete('/', '${this.domain}/${this.model}Controller.destroyAll').as('${this.endpoint}.destroyAll')
|
||||||
|
}).prefix('${this.endpoint}')
|
||||||
|
Route.resource('${this.endpoint}', '${this.domain}/${this.model}Controller').apiOnly()
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
19
commands/index.ts
Normal file
19
commands/index.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { listDirectoryFiles } from '@adonisjs/core/build/standalone'
|
||||||
|
import Application from '@ioc:Adonis/Core/Application'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Exporting an array of commands
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Instead of manually exporting each file from this directory, we use the
|
||||||
|
| helper `listDirectoryFiles` to recursively collect and export an array
|
||||||
|
| of filenames.
|
||||||
|
|
|
||||||
|
| Couple of things to note:
|
||||||
|
|
|
||||||
|
| 1. The file path must be relative from the project root and not this directory.
|
||||||
|
| 2. We must ignore this file to avoid getting into an infinite loop
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export default listDirectoryFiles(__dirname, Application.appRoot, ['./commands/index'])
|
||||||
34
config/ally.ts
Normal file
34
config/ally.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JOdi5
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import { AllyConfig } from '@ioc:Adonis/Addons/Ally'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Ally Config
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The `AllyConfig` relies on the `SocialProviders` interface which is
|
||||||
|
| defined inside `contracts/ally.ts` file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
const allyConfig: AllyConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Google driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
google: {
|
||||||
|
driver: 'google',
|
||||||
|
clientId: Env.get('GOOGLE_CLIENT_ID'),
|
||||||
|
clientSecret: Env.get('GOOGLE_CLIENT_SECRET'),
|
||||||
|
callbackUrl: 'http://localhost:3333/auth/google/callback',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default allyConfig
|
||||||
217
config/app.ts
Normal file
217
config/app.ts
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JfefZ
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import proxyAddr from 'proxy-addr'
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import { ServerConfig } from '@ioc:Adonis/Core/Server'
|
||||||
|
import { LoggerConfig } from '@ioc:Adonis/Core/Logger'
|
||||||
|
import { ProfilerConfig } from '@ioc:Adonis/Core/Profiler'
|
||||||
|
import { ValidatorConfig } from '@ioc:Adonis/Core/Validator'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application secret key
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The secret to encrypt and sign different values in your application.
|
||||||
|
| Make sure to keep the `APP_KEY` as an environment variable and secure.
|
||||||
|
|
|
||||||
|
| Note: Changing the application key for an existing app will make all
|
||||||
|
| the cookies invalid and also the existing encrypted data will not
|
||||||
|
| be decrypted.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const appKey: string = Env.get('APP_KEY')
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Http server configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The configuration for the HTTP(s) server. Make sure to go through all
|
||||||
|
| the config properties to make keep server secure.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const http: ServerConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Allow method spoofing
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Method spoofing enables defining custom HTTP methods using a query string
|
||||||
|
| `_method`. This is usually required when you are making traditional
|
||||||
|
| form requests and wants to use HTTP verbs like `PUT`, `DELETE` and
|
||||||
|
| so on.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
allowMethodSpoofing: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Subdomain offset
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
subdomainOffset: 2,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Request Ids
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Setting this value to `true` will generate a unique request id for each
|
||||||
|
| HTTP request and set it as `x-request-id` header.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
generateRequestId: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Trusting proxy servers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Define the proxy servers that AdonisJs must trust for reading `X-Forwarded`
|
||||||
|
| headers.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
trustProxy: proxyAddr.compile('loopback'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Generating Etag
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Whether or not to generate an etag for every response.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
etag: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| JSONP Callback
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
jsonpCallbackName: 'callback',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cookie settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
cookie: {
|
||||||
|
domain: '',
|
||||||
|
path: '/',
|
||||||
|
maxAge: '2h',
|
||||||
|
httpOnly: true,
|
||||||
|
secure: false,
|
||||||
|
sameSite: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Logger
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export const logger: LoggerConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application name
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The name of the application you want to add to the log. It is recommended
|
||||||
|
| to always have app name in every log line.
|
||||||
|
|
|
||||||
|
| The `APP_NAME` environment variable is automatically set by AdonisJS by
|
||||||
|
| reading the `name` property from the `package.json` file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
name: Env.get('APP_NAME'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Toggle logger
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Enable or disable logger application wide
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Logging level
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The level from which you want the logger to flush logs. It is recommended
|
||||||
|
| to make use of the environment variable, so that you can define log levels
|
||||||
|
| at deployment level and not code level.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
level: Env.get('LOG_LEVEL', 'info'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pretty print
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| It is highly advised NOT to use `prettyPrint` in production, since it
|
||||||
|
| can have huge impact on performance.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
prettyPrint: Env.get('NODE_ENV') === 'development',
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Profiler
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export const profiler: ProfilerConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Toggle profiler
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Enable or disable profiler
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Blacklist actions/row labels
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Define an array of actions or row labels that you want to disable from
|
||||||
|
| getting profiled.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
blacklist: [],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Whitelist actions/row labels
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Define an array of actions or row labels that you want to whitelist for
|
||||||
|
| the profiler. When whitelist is defined, then `blacklist` is ignored.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
whitelist: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Validator
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Configure the global configuration for the validator. Here's the reference
|
||||||
|
| to the default config https://git.io/JT0WE
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const validator: ValidatorConfig = {
|
||||||
|
}
|
||||||
109
config/auth.ts
Normal file
109
config/auth.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JY0mp
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AuthConfig } from '@ioc:Adonis/Addons/Auth'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Mapping
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| List of available authentication mapping. You must first define them
|
||||||
|
| inside the `contracts/auth.ts` file before mentioning them here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
const authConfig: AuthConfig = {
|
||||||
|
guard: 'api',
|
||||||
|
guards: {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| OAT Guard
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| OAT (Opaque access tokens) guard uses database backed tokens to authenticate
|
||||||
|
| HTTP request. This guard DOES NOT rely on sessions or cookies and uses
|
||||||
|
| Authorization header value for authentication.
|
||||||
|
|
|
||||||
|
| Use this guard to authenticate mobile apps or web clients that cannot rely
|
||||||
|
| on cookies/sessions.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
api: {
|
||||||
|
driver: 'oat',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Tokens provider
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Uses SQL database for managing tokens. Use the "database" driver, when
|
||||||
|
| tokens are the secondary mode of authentication.
|
||||||
|
| For example: The Github personal tokens
|
||||||
|
|
|
||||||
|
| The foreignKey column is used to make the relationship between the user
|
||||||
|
| and the token. You are free to use any column name here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
tokenProvider: {
|
||||||
|
type: 'api',
|
||||||
|
driver: 'database',
|
||||||
|
table: 'user.api_tokens',
|
||||||
|
foreignKey: 'user_id',
|
||||||
|
},
|
||||||
|
|
||||||
|
provider: {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name of the driver
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
driver: 'lucid',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Identifier key
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The identifier key is the unique key on the model. In most cases specifying
|
||||||
|
| the primary key is the right choice.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
identifierKey: 'id',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Uids
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Uids are used to search a user against one of the mentioned columns. During
|
||||||
|
| login, the auth module will search the user mentioned value against one
|
||||||
|
| of the mentioned columns to find their user record.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
uids: ['email'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Model
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The model to use for fetching or finding users. The model is imported
|
||||||
|
| lazily since the config files are read way earlier in the lifecycle
|
||||||
|
| of booting the app and the models may not be in a usable state at
|
||||||
|
| that time.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
model: () => import('App/Models/User/Account'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default authConfig
|
||||||
211
config/bodyparser.ts
Normal file
211
config/bodyparser.ts
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/Jfefn
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BodyParserConfig } from '@ioc:Adonis/Core/BodyParser'
|
||||||
|
|
||||||
|
const bodyParserConfig: BodyParserConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| White listed methods
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| HTTP methods for which body parsing must be performed. It is a good practice
|
||||||
|
| to avoid body parsing for `GET` requests.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
whitelistedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| JSON parser settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The settings for the JSON parser. The types defines the request content
|
||||||
|
| types which gets processed by the JSON parser.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
json: {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
limit: '1mb',
|
||||||
|
strict: true,
|
||||||
|
types: [
|
||||||
|
'application/json',
|
||||||
|
'application/json-patch+json',
|
||||||
|
'application/vnd.api+json',
|
||||||
|
'application/csp-report',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Form parser settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The settings for the `application/x-www-form-urlencoded` parser. The types
|
||||||
|
| defines the request content types which gets processed by the form parser.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
form: {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
limit: '1mb',
|
||||||
|
queryString: {},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Convert empty strings to null
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Convert empty form fields to null. HTML forms results in field string
|
||||||
|
| value when the field is left blank. This option normalizes all the blank
|
||||||
|
| field values to "null"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
convertEmptyStringsToNull: true,
|
||||||
|
|
||||||
|
types: [
|
||||||
|
'application/x-www-form-urlencoded',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Raw body parser settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Raw body just reads the request body stream as a plain text, which you
|
||||||
|
| can process by hand. This must be used when request body type is not
|
||||||
|
| supported by the body parser.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
raw: {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
limit: '1mb',
|
||||||
|
queryString: {},
|
||||||
|
types: [
|
||||||
|
'text/*',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Multipart parser settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The settings for the `multipart/form-data` parser. The types defines the
|
||||||
|
| request content types which gets processed by the form parser.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
multipart: {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Auto process
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The auto process option will process uploaded files and writes them to
|
||||||
|
| the `tmp` folder. You can turn it off and then manually use the stream
|
||||||
|
| to pipe stream to a different destination.
|
||||||
|
|
|
||||||
|
| It is recommended to keep `autoProcess=true`. Unless you are processing bigger
|
||||||
|
| file sizes.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
autoProcess: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Files to be processed manually
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can turn off `autoProcess` for certain routes by defining
|
||||||
|
| routes inside the following array.
|
||||||
|
|
|
||||||
|
| NOTE: Make sure the route pattern starts with a leading slash.
|
||||||
|
|
|
||||||
|
| Correct
|
||||||
|
| ```js
|
||||||
|
| /projects/:id/file
|
||||||
|
| ```
|
||||||
|
|
|
||||||
|
| Incorrect
|
||||||
|
| ```js
|
||||||
|
| projects/:id/file
|
||||||
|
| ```
|
||||||
|
*/
|
||||||
|
processManually: [],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Temporary file name
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When auto processing is on. We will use this method to compute the temporary
|
||||||
|
| file name. AdonisJs will compute a unique `tmpPath` for you automatically,
|
||||||
|
| However, you can also define your own custom method.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
// tmpFileName () {
|
||||||
|
// },
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Encoding
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Request body encoding
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
encoding: 'utf-8',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Convert empty strings to null
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Convert empty form fields to null. HTML forms results in field string
|
||||||
|
| value when the field is left blank. This option normalizes all the blank
|
||||||
|
| field values to "null"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
convertEmptyStringsToNull: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Max Fields
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The maximum number of fields allowed in the request body. The field includes
|
||||||
|
| text inputs and files both.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
maxFields: 1000,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Request body limit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The total limit to the multipart body. This includes all request files
|
||||||
|
| and fields data.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
limit: '20mb',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Types
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The types that will be considered and parsed as multipart body.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
types: [
|
||||||
|
'multipart/form-data',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default bodyParserConfig
|
||||||
134
config/cors.ts
Normal file
134
config/cors.ts
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JfefC
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CorsConfig } from '@ioc:Adonis/Core/Cors'
|
||||||
|
|
||||||
|
const corsConfig: CorsConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enabled
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| A boolean to enable or disable CORS integration from your AdonisJs
|
||||||
|
| application.
|
||||||
|
|
|
||||||
|
| Setting the value to `true` will enable the CORS for all HTTP request. However,
|
||||||
|
| you can define a function to enable/disable it on per request basis as well.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
|
||||||
|
// You can also use a function that return true or false.
|
||||||
|
// enabled: (request) => request.url().startsWith('/api')
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Origin
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Set a list of origins to be allowed for `Access-Control-Allow-Origin`.
|
||||||
|
| The value can be one of the following:
|
||||||
|
|
|
||||||
|
| https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||||
|
|
|
||||||
|
| Boolean (true) - Allow current request origin.
|
||||||
|
| Boolean (false) - Disallow all.
|
||||||
|
| String - Comma separated list of allowed origins.
|
||||||
|
| Array - An array of allowed origins.
|
||||||
|
| String (*) - A wildcard (*) to allow all request origins.
|
||||||
|
| Function - Receives the current origin string and should return
|
||||||
|
| one of the above values.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
origin: '*',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Methods
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| An array of allowed HTTP methods for CORS. The `Access-Control-Request-Method`
|
||||||
|
| is checked against the following list.
|
||||||
|
|
|
||||||
|
| Following is the list of default methods. Feel free to add more.
|
||||||
|
*/
|
||||||
|
methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Headers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| List of headers to be allowed for `Access-Control-Allow-Headers` header.
|
||||||
|
| The value can be one of the following:
|
||||||
|
|
|
||||||
|
| https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers
|
||||||
|
|
|
||||||
|
| Boolean(true) - Allow all headers mentioned in `Access-Control-Request-Headers`.
|
||||||
|
| Boolean(false) - Disallow all headers.
|
||||||
|
| String - Comma separated list of allowed headers.
|
||||||
|
| Array - An array of allowed 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 by setting `Access-Control-Expose-Headers`.
|
||||||
|
| header. By default following 6 simple response headers are exposed.
|
||||||
|
|
|
||||||
|
| Cache-Control
|
||||||
|
| Content-Language
|
||||||
|
| Content-Type
|
||||||
|
| Expires
|
||||||
|
| Last-Modified
|
||||||
|
| Pragma
|
||||||
|
|
|
||||||
|
| In order to add more headers, simply define them inside the following array.
|
||||||
|
|
|
||||||
|
| https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
exposeHeaders: [
|
||||||
|
'cache-control',
|
||||||
|
'content-language',
|
||||||
|
'content-type',
|
||||||
|
'expires',
|
||||||
|
'last-modified',
|
||||||
|
'pragma',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credentials
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Toggle `Access-Control-Allow-Credentials` header. If value is set to `true`,
|
||||||
|
| then header will be set, otherwise not.
|
||||||
|
|
|
||||||
|
| https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
credentials: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| MaxAge
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Define `Access-Control-Max-Age` header in seconds.
|
||||||
|
| https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
maxAge: 90,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default corsConfig
|
||||||
55
config/database.ts
Normal file
55
config/database.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JesV9
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import { DatabaseConfig } from '@ioc:Adonis/Lucid/Database'
|
||||||
|
|
||||||
|
const databaseConfig: DatabaseConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Connection
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The primary connection for making database queries across the application
|
||||||
|
| You can use any key from the `connections` object defined in this same
|
||||||
|
| file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
connection: Env.get('DB_CONNECTION'),
|
||||||
|
|
||||||
|
connections: {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| PostgreSQL config
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Configuration for PostgreSQL database. Make sure to install the driver
|
||||||
|
| from npm when using this connection
|
||||||
|
|
|
||||||
|
| npm i pg
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
pg: {
|
||||||
|
client: 'pg',
|
||||||
|
connection: {
|
||||||
|
host: Env.get('PG_HOST'),
|
||||||
|
port: Env.get('PG_PORT'),
|
||||||
|
user: Env.get('PG_USER'),
|
||||||
|
password: Env.get('PG_PASSWORD', ''),
|
||||||
|
database: Env.get('PG_DB_NAME'),
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
naturalSort: true,
|
||||||
|
},
|
||||||
|
healthCheck: false,
|
||||||
|
debug: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default databaseConfig
|
||||||
75
config/hash.ts
Normal file
75
config/hash.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JfefW
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import { HashConfig } from '@ioc:Adonis/Core/Hash'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Hash Config
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The `HashConfig` relies on the `HashList` interface which is
|
||||||
|
| defined inside `contracts` directory.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
const hashConfig: HashConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default hasher
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| By default we make use of the bcrypt hasher to hash values. However, feel
|
||||||
|
| free to change the default value
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
default: Env.get('HASH_DRIVER', 'argon'),
|
||||||
|
|
||||||
|
list: {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Argon
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Argon mapping uses the `argon2` driver to hash values.
|
||||||
|
|
|
||||||
|
| Make sure you install the underlying dependency for this driver to work.
|
||||||
|
| https://www.npmjs.com/package/phc-argon2.
|
||||||
|
|
|
||||||
|
| npm install phc-argon2
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
argon: {
|
||||||
|
driver: 'argon2',
|
||||||
|
variant: 'id',
|
||||||
|
iterations: 3,
|
||||||
|
memory: 4096,
|
||||||
|
parallelism: 1,
|
||||||
|
saltSize: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Bcrypt
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Bcrypt mapping uses the `bcrypt` driver to hash values.
|
||||||
|
|
|
||||||
|
| Make sure you install the underlying dependency for this driver to work.
|
||||||
|
| https://www.npmjs.com/package/phc-bcrypt.
|
||||||
|
|
|
||||||
|
| npm install phc-bcrypt
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
bcrypt: {
|
||||||
|
driver: 'bcrypt',
|
||||||
|
rounds: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default hashConfig
|
||||||
59
config/mail.ts
Normal file
59
config/mail.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JvgAf
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this contract
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import { MailConfig } from '@ioc:Adonis/Addons/Mail'
|
||||||
|
|
||||||
|
const mailConfig: MailConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default mailer
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following mailer will be used to send emails, when you don't specify
|
||||||
|
| a mailer
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
mailer: 'smtp',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Mailers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can define or more mailers to send emails from your application. A
|
||||||
|
| single `driver` can be used to define multiple mailers with different
|
||||||
|
| config.
|
||||||
|
|
|
||||||
|
| For example: Postmark driver can be used to have different mailers for
|
||||||
|
| sending transactional and promotional emails
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
mailers: {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Smtp
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Uses SMTP protocol for sending email
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
smtp: {
|
||||||
|
driver: 'smtp',
|
||||||
|
host: Env.get('SMTP_HOST'),
|
||||||
|
port: Env.get('SMTP_PORT'),
|
||||||
|
auth: {
|
||||||
|
user: Env.get('SMTP_USERNAME'),
|
||||||
|
pass: Env.get('SMTP_PASSWORD'),
|
||||||
|
type: 'login',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default mailConfig
|
||||||
118
config/session.ts
Normal file
118
config/session.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/JeYHp
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import Application from '@ioc:Adonis/Core/Application'
|
||||||
|
import { SessionConfig } from '@ioc:Adonis/Addons/Session'
|
||||||
|
|
||||||
|
const sessionConfig: SessionConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable/Disable sessions
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Setting the following property to "false" will disable the session for the
|
||||||
|
| entire application
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The session driver to use. You can choose between one of the following
|
||||||
|
| drivers.
|
||||||
|
|
|
||||||
|
| - cookie (Uses signed cookies to store session values)
|
||||||
|
| - file (Uses filesystem to store session values)
|
||||||
|
| - redis (Uses redis. Make sure to install "@adonisjs/redis" as well)
|
||||||
|
|
|
||||||
|
| Note: Switching drivers will make existing sessions invalid.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
driver: Env.get('SESSION_DRIVER'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cookie name
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The name of the cookie that will hold the session id.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
cookieName: 'adonis-session',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Clear session when browser closes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Whether or not you want to destroy the session when browser closes. Setting
|
||||||
|
| this value to `true` will ignore the `age`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
clearWithBrowser: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Session age
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The duration for which session stays active after no activity. A new HTTP
|
||||||
|
| request to the server is considered as activity.
|
||||||
|
|
|
||||||
|
| The value can be a number in milliseconds or a string that must be valid
|
||||||
|
| as per https://npmjs.org/package/ms package.
|
||||||
|
|
|
||||||
|
| Example: `2 days`, `2.5 hrs`, `1y`, `5s` and so on.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
age: '2h',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cookie values
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The cookie settings are used to setup the session id cookie and also the
|
||||||
|
| driver will use the same values.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
cookie: {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Configuration for the file driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The file driver needs absolute path to the directory in which sessions
|
||||||
|
| must be stored.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
file: {
|
||||||
|
location: Application.tmpPath('sessions'),
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Redis driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The redis connection you want session driver to use. The same connection
|
||||||
|
| must be defined inside `config/redis.ts` file as well.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
redisConnection: 'local',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default sessionConfig
|
||||||
238
config/shield.ts
Normal file
238
config/shield.ts
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/Jvwvt
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
import { ShieldConfig } from '@ioc:Adonis/Addons/Shield'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| 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.
|
||||||
|
*/
|
||||||
|
export const csp: ShieldConfig['csp'] = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable/disable CSP
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The CSP rules are disabled by default for seamless onboarding.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enabled: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| CSRF Protection
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| CSRF Protection adds another layer of security by making sure, actionable
|
||||||
|
| routes does have a valid token to execute an action.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const csrf: ShieldConfig['csrf'] = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable/Disable CSRF
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
enabled: Env.get('NODE_ENV') !== 'testing',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Routes to Ignore
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Define an array of route patterns that you want to ignore from CSRF
|
||||||
|
| validation. Make sure the route patterns are started with a leading
|
||||||
|
| slash. Example:
|
||||||
|
|
|
||||||
|
| `/foo/bar`
|
||||||
|
|
|
||||||
|
| Also you can define a function that is evaluated on every HTTP Request.
|
||||||
|
| ```
|
||||||
|
| exceptRoutes: ({ request }) => request.url().includes('/api')
|
||||||
|
| ```
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
exceptRoutes: [],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable Sharing Token Via Cookie
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When the following flag is enabled, AdonisJS will drop `XSRF-TOKEN`
|
||||||
|
| cookie that frontend frameworks can read and return back as a
|
||||||
|
| `X-XSRF-TOKEN` header.
|
||||||
|
|
|
||||||
|
| The cookie has `httpOnly` flag set to false, so it is little insecure and
|
||||||
|
| can be turned off when you are not using a frontend framework making
|
||||||
|
| AJAX requests.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enableXsrfCookie: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Methods to Validate
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Define an array of HTTP methods to be validated for a valid CSRF token.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
methods: ['POST', 'PUT', 'PATCH', 'DELETE'],
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| DNS Prefetching
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| DNS prefetching allows browsers to proactively perform domain name
|
||||||
|
| resolution in background.
|
||||||
|
|
|
||||||
|
| Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const dnsPrefetch: ShieldConfig['dnsPrefetch'] = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable/disable this feature
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Allow or Dis-Allow Explicitly
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The `enabled` boolean does not set `X-DNS-Prefetch-Control` header. However
|
||||||
|
| the `allow` boolean controls the value of `X-DNS-Prefetch-Control` header.
|
||||||
|
|
|
||||||
|
| - When `allow = true`, then `X-DNS-Prefetch-Control = 'on'`
|
||||||
|
| - When `allow = false`, then `X-DNS-Prefetch-Control = 'off'`
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
allow: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Iframe Options
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| xFrame defines whether or not your website can be embedded inside an
|
||||||
|
| iframe. Choose from one of the following options.
|
||||||
|
|
|
||||||
|
| - DENY
|
||||||
|
| - SAMEORIGIN
|
||||||
|
| - ALLOW-FROM http://example.com
|
||||||
|
|
|
||||||
|
| Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||||
|
*/
|
||||||
|
export const xFrame: ShieldConfig['xFrame'] = {
|
||||||
|
enabled: true,
|
||||||
|
action: 'DENY',
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Http Strict Transport Security
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| A security to ensure that a browser always makes a connection over
|
||||||
|
| HTTPS.
|
||||||
|
|
|
||||||
|
| Learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const hsts: ShieldConfig['hsts'] = {
|
||||||
|
enabled: true,
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Max Age
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Control, how long the browser should remember that a site is only to be
|
||||||
|
| accessed using HTTPS.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
maxAge: '180 days',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Include Subdomains
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Apply rules on the subdomains as well.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
includeSubDomains: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Preloading
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Google maintains a service to register your domain and it will preload
|
||||||
|
| the HSTS policy. Learn more https://hstspreload.org/
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
preload: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| 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
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
export const contentTypeSniffing: ShieldConfig['contentTypeSniffing'] = {
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
64
config/static.ts
Normal file
64
config/static.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Config source: https://git.io/Jfefl
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AssetsConfig } from '@ioc:Adonis/Core/Static'
|
||||||
|
|
||||||
|
const staticConfig: AssetsConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enabled
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| A boolean to enable or disable serving static files. The static files
|
||||||
|
| are served from the `public` directory inside the application root.
|
||||||
|
| However, you can override the default path inside `.adonisrc.json`
|
||||||
|
| file.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Handling Dot Files
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Decide how you want the static assets server to handle the `dotfiles`.
|
||||||
|
| By default, we ignore them as if they don't exists. However, you
|
||||||
|
| can choose between one of the following options.
|
||||||
|
|
|
||||||
|
| - ignore: Behave as if the file doesn't exists. Results in 404.
|
||||||
|
| - deny: Deny access to the file. Results in 403.
|
||||||
|
| - allow: Serve the file contents
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
dotFiles: 'ignore',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Generating Etag
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Handle whether or not to generate etags for the files. Etag allows browser
|
||||||
|
| to utilize the cache when file hasn't been changed.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
etag: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Set Last Modified
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Whether or not to set the `Last-Modified` header in the response. Uses
|
||||||
|
| the file system's last modified value.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
lastModified: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default staticConfig
|
||||||
15
contracts/ally.ts
Normal file
15
contracts/ally.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Contract source: https://git.io/JOdiQ
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this contract
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '@ioc:Adonis/Addons/Ally' {
|
||||||
|
interface SocialProviders {
|
||||||
|
google: {
|
||||||
|
config: GoogleDriverConfig
|
||||||
|
implementation: GoogleDriverContract
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
contracts/auth.ts
Normal file
72
contracts/auth.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Contract source: https://git.io/JOdz5
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Account from 'App/Models/Account'
|
||||||
|
|
||||||
|
declare module '@ioc:Adonis/Addons/Auth' {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Providers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The providers are used to fetch users. The Auth module comes pre-bundled
|
||||||
|
| with two providers that are `Lucid` and `Database`. Both uses database
|
||||||
|
| to fetch user details.
|
||||||
|
|
|
||||||
|
| You can also create and register your own custom providers.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
interface ProvidersList {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| User Provider
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following provider uses Lucid models as a driver for fetching user
|
||||||
|
| details from the database for authentication.
|
||||||
|
|
|
||||||
|
| You can create multiple providers using the same underlying driver with
|
||||||
|
| different Lucid models.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
user: {
|
||||||
|
implementation: LucidProviderContract<typeof Account>
|
||||||
|
config: LucidProviderConfig<typeof Account>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Guards
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The guards are used for authenticating users using different drivers.
|
||||||
|
| The auth module comes with 3 different guards.
|
||||||
|
|
|
||||||
|
| - SessionGuardContract
|
||||||
|
| - BasicAuthGuardContract
|
||||||
|
| - OATGuardContract ( Opaque access token )
|
||||||
|
|
|
||||||
|
| Every guard needs a provider for looking up users from the database.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
interface GuardsList {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| OAT Guard
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| OAT, stands for (Opaque access tokens) guard uses database backed tokens
|
||||||
|
| to authenticate requests.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
api: {
|
||||||
|
implementation: OATGuardContract<'user', 'api'>
|
||||||
|
config: OATGuardConfig<'user'>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
contracts/env.ts
Normal file
24
contracts/env.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Contract source: https://git.io/JTm6U
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this contract
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '@ioc:Adonis/Core/Env' {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Getting types for validated environment variables
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The `default` export from the "../env.ts" file exports types for the
|
||||||
|
| validated environment variables. Here we merge them with the `EnvTypes`
|
||||||
|
| interface so that you can enjoy intellisense when using the "Env"
|
||||||
|
| module.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
type CustomTypes = typeof import("../env").default;
|
||||||
|
interface EnvTypes extends CustomTypes {
|
||||||
|
}
|
||||||
|
}
|
||||||
30
contracts/events.ts
Normal file
30
contracts/events.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Contract source: https://git.io/JfefG
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this contract
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '@ioc:Adonis/Core/Event' {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Define typed events
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can define types for events inside the following interface and
|
||||||
|
| AdonisJS will make sure that all listeners and emit calls adheres
|
||||||
|
| to the defined types.
|
||||||
|
|
|
||||||
|
| For example:
|
||||||
|
|
|
||||||
|
| interface EventsList {
|
||||||
|
| 'new:user': UserModel
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
| Now calling `Event.emit('new:user')` will statically ensure that passed value is
|
||||||
|
| an instance of the the UserModel only.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
interface EventsList {
|
||||||
|
}
|
||||||
|
}
|
||||||
19
contracts/hash.ts
Normal file
19
contracts/hash.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Contract source: https://git.io/Jfefs
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this contract
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '@ioc:Adonis/Core/Hash' {
|
||||||
|
interface HashersList {
|
||||||
|
bcrypt: {
|
||||||
|
config: BcryptConfig,
|
||||||
|
implementation: BcryptContract,
|
||||||
|
},
|
||||||
|
argon: {
|
||||||
|
config: ArgonConfig,
|
||||||
|
implementation: ArgonContract,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
14
contracts/mail.ts
Normal file
14
contracts/mail.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* Contract source: https://git.io/JvgAT
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this contract
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '@ioc:Adonis/Addons/Mail' {
|
||||||
|
import { MailDrivers } from '@ioc:Adonis/Addons/Mail'
|
||||||
|
|
||||||
|
interface MailersList {
|
||||||
|
smtp: MailDrivers['smtp'],
|
||||||
|
}
|
||||||
|
}
|
||||||
5
contracts/request.ts
Normal file
5
contracts/request.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
declare module '@ioc:Adonis/Core/Request' {
|
||||||
|
interface RequestContract {
|
||||||
|
parseParams(request: any): any
|
||||||
|
}
|
||||||
|
}
|
||||||
6
contracts/response.ts
Normal file
6
contracts/response.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
declare module '@ioc:Adonis/Core/Response' {
|
||||||
|
interface ResponseContract {
|
||||||
|
api(data?: any | null, message?: string | null, code?: number | null, request?: any | null): any,
|
||||||
|
error(message?: string | null, errors?: any | null, code?: number | null): any
|
||||||
|
}
|
||||||
|
}
|
||||||
1
database/factories/index.ts
Normal file
1
database/factories/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
// import Factory from '@ioc:Adonis/Lucid/Factory'
|
||||||
31
database/sql-only/support_function/alpha_requirement.sql
Normal file
31
database/sql-only/support_function/alpha_requirement.sql
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
-- CREATE DIRECTORY FOR TABLESPACE
|
||||||
|
# sudo mkdir /data/postgres/tablespaces/ts_pg_roadreport_dev
|
||||||
|
|
||||||
|
-- GRANT PERMISSION ON TS FOR postgres
|
||||||
|
# sudo chown postgres:postgres /data/postgres/tablespaces/ts_pg_roadreport_dev
|
||||||
|
-- CREATE TABLESPACE FOR DATA
|
||||||
|
|
||||||
|
CREATE TABLESPACE "ts_pg_roadreport_dev"
|
||||||
|
OWNER postgres
|
||||||
|
LOCATION '/data/postgres/tablespaces/ts_pg_roadreport_dev';
|
||||||
|
|
||||||
|
|
||||||
|
-- CREATE DATABASE
|
||||||
|
|
||||||
|
CREATE DATABASE "pg_roadreport_dev"
|
||||||
|
WITH
|
||||||
|
OWNER = postgres
|
||||||
|
ENCODING = 'UTF8'
|
||||||
|
TABLESPACE = "ts_pg_roadreport_dev"
|
||||||
|
CONNECTION LIMIT = -1;
|
||||||
|
|
||||||
|
-- ENABLE UUID SUPPORT
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
|
-- CREATE USER
|
||||||
|
CREATE ROLE "roadreportpgdb" LOGIN PASSWORD 'roadreportpgdb1qaz';
|
||||||
|
|
||||||
|
-- ADD USER ACCESS IN PG HBA_CONF
|
||||||
|
|
||||||
|
-- Refresh PG_CONF
|
||||||
|
SELECT pg_reload_conf();
|
||||||
7
database/sql-only/support_function/grant_access.sql
Normal file
7
database/sql-only/support_function/grant_access.sql
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- === Grant Script on database---
|
||||||
|
|
||||||
|
-- === GRANT PUBLIC SCHEMA ==== --
|
||||||
|
GRANT USAGE ON SCHEMA "public" TO roadreportpgdb;
|
||||||
|
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO roadreportpgdb;
|
||||||
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO roadreportpgdb;
|
||||||
|
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO roadreportpgdb;
|
||||||
114
database/sql-only/user-schema/ddl_table_user_schema.sql
Normal file
114
database/sql-only/user-schema/ddl_table_user_schema.sql
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
-- =============START USER SCHEMA==================
|
||||||
|
|
||||||
|
DROP SCHEMA IF EXISTS "user";
|
||||||
|
CREATE SCHEMA "user"
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for role
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS "user"."role" CASCADE;
|
||||||
|
|
||||||
|
-- Ver 0.1.0 ( Design )
|
||||||
|
-- Ver 0.1.0 ( Current & Implemented )
|
||||||
|
CREATE TABLE "user"."role"(
|
||||||
|
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"code" VARCHAR(5) NOT NULL ,
|
||||||
|
"name" VARCHAR(100) NOT NULL ,
|
||||||
|
"desc" TEXT NULL ,
|
||||||
|
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
|
||||||
|
"updated_at" TIMESTAMP NULL ,
|
||||||
|
PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- INDEX TABLE role
|
||||||
|
CREATE INDEX "pkey_urole" ON "user"."role" ("id");
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for account
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS "user"."account" CASCADE;
|
||||||
|
|
||||||
|
-- Ver 0.1.0 ( Design )
|
||||||
|
-- Ver 0.1.0 ( Current & Implemented )
|
||||||
|
CREATE TABLE "user"."account"(
|
||||||
|
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"urole_id" UUID NOT NULL ,
|
||||||
|
"username" varchar(255) NOT NULL UNIQUE,
|
||||||
|
"pwd" TEXT NOT NULL ,
|
||||||
|
"fullname" varchar(255) NOT NULL ,
|
||||||
|
"shortname" varchar(255) NOT NULL ,
|
||||||
|
"email" varchar(255) NOT NULL UNIQUE ,
|
||||||
|
"avatar" varchar(255) NOT NULL ,
|
||||||
|
"note" varchar(255) NULL ,
|
||||||
|
"status" BOOLEAN NOT NULL DEFAULT FALSE ,
|
||||||
|
"is_ban" BOOLEAN NOT NULL DEFAULT FALSE ,
|
||||||
|
"last_active" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
|
||||||
|
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
|
||||||
|
"updated_at" TIMESTAMP NULL ,
|
||||||
|
"deleted_at" TIMESTAMP NULL ,
|
||||||
|
PRIMARY KEY ("id"),
|
||||||
|
FOREIGN KEY ("urole_id") REFERENCES "user"."role"("id") ON UPDATE CASCADE ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- INDEX TABLE account
|
||||||
|
CREATE INDEX "pkey_uaccount" ON "user"."account" ("id");
|
||||||
|
CREATE INDEX "fkey_uaccount_urole" ON "user"."account" ("urole_id");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for api_tokens
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS "user"."api_tokens" CASCADE;
|
||||||
|
|
||||||
|
-- Ver 0.1.0 ( Design )
|
||||||
|
-- Ver 0.1.0 ( Current & Implemented )
|
||||||
|
CREATE TABLE "user"."api_tokens"(
|
||||||
|
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"user_id" UUID NOT NULL ,
|
||||||
|
"name" VARCHAR(255) NOT NULL ,
|
||||||
|
"type" VARCHAR(255) NOT NULL ,
|
||||||
|
"token" VARCHAR(255) NOT NULL ,
|
||||||
|
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"expires_at" TIMESTAMP ,
|
||||||
|
PRIMARY KEY ("id"),
|
||||||
|
FOREIGN KEY ("user_id") REFERENCES "user"."account"("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- INDEX TABLE account
|
||||||
|
CREATE INDEX "pkey_uapi_tokens" ON "user"."api_tokens" ("id");
|
||||||
|
CREATE INDEX "fkey_uapi_tokens_uaccount" ON "user"."api_tokens" ("user_id");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for event
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS "user"."event" CASCADE;
|
||||||
|
DROP SEQUENCE IF EXISTS event_id_seq;
|
||||||
|
|
||||||
|
-- Ver 1
|
||||||
|
-- ( Current & Implemented )
|
||||||
|
CREATE TABLE "user"."event"(
|
||||||
|
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"uaccount_id" UUID NOT NULL ,
|
||||||
|
"project_id" UUID NULL ,
|
||||||
|
"fullname" VARCHAR NOT NULL ,
|
||||||
|
"title" VARCHAR NOT NULL ,
|
||||||
|
"body" TEXT NULL ,
|
||||||
|
"start" DATE NOT NULL ,
|
||||||
|
"end" DATE NOT NULL ,
|
||||||
|
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP NULL,
|
||||||
|
"deleted_at" TIMESTAMP NULL,
|
||||||
|
PRIMARY KEY ("id"),
|
||||||
|
FOREIGN KEY ("uaccount_id") REFERENCES "user"."account"("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- =============END USER SCHEMA==================
|
||||||
31
env.ts
Normal file
31
env.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Validating Environment Variables
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| In this file we define the rules for validating environment variables.
|
||||||
|
| By performing validation we ensure that your application is running in
|
||||||
|
| a stable environment with correct configuration values.
|
||||||
|
|
|
||||||
|
| This file is read automatically by the framework during the boot lifecycle
|
||||||
|
| and hence do not rename or move this file to a different location.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
|
||||||
|
export default Env.rules({
|
||||||
|
HOST: Env.schema.string({ format: 'host' }),
|
||||||
|
PORT: Env.schema.number(),
|
||||||
|
APP_KEY: Env.schema.string(),
|
||||||
|
APP_NAME: Env.schema.string(),
|
||||||
|
CACHE_VIEWS: Env.schema.boolean(),
|
||||||
|
SESSION_DRIVER: Env.schema.string(),
|
||||||
|
NODE_ENV: Env.schema.enum(['development', 'production', 'testing'] as const),
|
||||||
|
SMTP_HOST: Env.schema.string({ format: 'host' }),
|
||||||
|
SMTP_PORT: Env.schema.number(),
|
||||||
|
SMTP_USERNAME: Env.schema.string(),
|
||||||
|
SMTP_PASSWORD: Env.schema.string(),
|
||||||
|
GOOGLE_CLIENT_ID: Env.schema.string(),
|
||||||
|
GOOGLE_CLIENT_SECRET: Env.schema.string(),
|
||||||
|
})
|
||||||
40
package.json
Normal file
40
package.json
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "roadreport-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "node ace build --production",
|
||||||
|
"start": "node server.js",
|
||||||
|
"dev": "node ace serve --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@adonisjs/assembler": "^5.3.5",
|
||||||
|
"@types/uuid": "^8.3.1",
|
||||||
|
"adonis-preset-ts": "^2.1.0",
|
||||||
|
"pino-pretty": "^5.1.2",
|
||||||
|
"typescript": "^4.2.4",
|
||||||
|
"youch": "^2.2.2",
|
||||||
|
"youch-terminal": "^1.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@adonisjs/ally": "^4.1.1",
|
||||||
|
"@adonisjs/auth": "^8.2.1",
|
||||||
|
"@adonisjs/core": "^5.1.11",
|
||||||
|
"@adonisjs/lucid": "^18.0.0",
|
||||||
|
"@adonisjs/mail": "^7.2.4",
|
||||||
|
"@adonisjs/repl": "^3.1.5",
|
||||||
|
"@adonisjs/session": "^6.1.1",
|
||||||
|
"@adonisjs/shield": "^7.0.5",
|
||||||
|
"@adonisjs/view": "^6.0.8",
|
||||||
|
"base-64": "^1.0.0",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"luxon": "^2.0.2",
|
||||||
|
"moment": "^2.29.1",
|
||||||
|
"pg": "^8.7.1",
|
||||||
|
"phc-argon2": "^1.1.2",
|
||||||
|
"proxy-addr": "^2.0.7",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"source-map-support": "^0.5.19",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
94
providers/AppProvider.ts
Normal file
94
providers/AppProvider.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
|
||||||
|
import ParseParamService from 'App/Base/Services/ParseParamService'
|
||||||
|
import ParseUrlService from 'App/Base/Services/ParseUrlService'
|
||||||
|
|
||||||
|
export default class AppProvider {
|
||||||
|
public static needsApplication = true
|
||||||
|
constructor (protected app: ApplicationContract) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public register () {
|
||||||
|
// Register your own bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
public async boot () {
|
||||||
|
// IoC container is ready
|
||||||
|
this.extendRequest()
|
||||||
|
this.extendResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ready () {
|
||||||
|
// App is ready
|
||||||
|
}
|
||||||
|
|
||||||
|
public async shutdown () {
|
||||||
|
// Cleanup, since app is going down
|
||||||
|
}
|
||||||
|
|
||||||
|
extendResponse() {
|
||||||
|
const Response = this.app.container.use('Adonis/Core/Response')
|
||||||
|
|
||||||
|
Response.macro('api', function (data, message = 'OK', code: any = 200, request = null) {
|
||||||
|
const parseUrlService = new ParseUrlService()
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
if (data.rows) {
|
||||||
|
const url = parseUrlService.parseUrl(request, data.currentPage ?? 1, data.lastPage ?? 1)
|
||||||
|
|
||||||
|
this.status(code).json({
|
||||||
|
data: data.rows,
|
||||||
|
page: data.currentPage ?? 1,
|
||||||
|
total: data.total ? parseInt(data.total) : data.rows.length,
|
||||||
|
perPage: data.perPage ?? data.rows.length,
|
||||||
|
lastPage: data.lastPage ?? 1,
|
||||||
|
nextPage: url.nextUrl,
|
||||||
|
previousPage: url.prevUrl,
|
||||||
|
statusCode: code,
|
||||||
|
message: message
|
||||||
|
})
|
||||||
|
} else if (data.length || Array.isArray(data)) {
|
||||||
|
this.status(code).json({
|
||||||
|
data: data,
|
||||||
|
page: 1,
|
||||||
|
total: data.length,
|
||||||
|
perPage: data.length,
|
||||||
|
lastPage: 1,
|
||||||
|
nextPage: null,
|
||||||
|
previousPage: null,
|
||||||
|
statusCode: code,
|
||||||
|
message: message
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.status(code).json({
|
||||||
|
data: data,
|
||||||
|
statusCode: code,
|
||||||
|
message: message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.status(code).json({
|
||||||
|
data: null,
|
||||||
|
statusCode: code,
|
||||||
|
message: message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Response.macro('error', function (message, errors = null, code: any = 400) {
|
||||||
|
this.status(code).json({
|
||||||
|
statusCode: code,
|
||||||
|
message: message,
|
||||||
|
errors: errors
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
extendRequest() {
|
||||||
|
const Request = this.app.container.use('Adonis/Core/Request')
|
||||||
|
|
||||||
|
Request.macro('parseParams', function (request) {
|
||||||
|
const parseParamService = new ParseParamService()
|
||||||
|
return parseParamService.parse(request)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
3
resources/views/emails/restore_password.edge
Normal file
3
resources/views/emails/restore_password.edge
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<p>
|
||||||
|
token: {{token}}
|
||||||
|
</p>
|
||||||
1
resources/views/errors/not-found.edge
Normal file
1
resources/views/errors/not-found.edge
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<p> It's a 404 </p>
|
||||||
1
resources/views/errors/server-error.edge
Normal file
1
resources/views/errors/server-error.edge
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<p> It's a 500 </p>
|
||||||
1
resources/views/errors/unauthorized.edge
Normal file
1
resources/views/errors/unauthorized.edge
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<p> It's a 403 </p>
|
||||||
110
resources/views/index.edge
Normal file
110
resources/views/index.edge
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AdonisJS - A fully featured web framework for Node.js</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Poppins:400,500&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
background-color: #F7F8FA;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
color: #46444c;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:before {
|
||||||
|
content: '';
|
||||||
|
background: #5A45FF;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 6px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5A45FF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 620px;
|
||||||
|
margin: auto;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 0 30px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #17161A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 26px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main li:before {
|
||||||
|
content: '—';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main code {
|
||||||
|
font-size: 16px;
|
||||||
|
background: #e6e2ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div>
|
||||||
|
<h1 class="title"> It Works! </h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
Congratulations, you have just created your first AdonisJS app.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
The route for this page is defined inside <code>start/routes.ts</code> file
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
You can update this page by editing <code>resources/views/welcome.edge</code> file
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
If you run into problems, you can reach us on <a href="https://discord.gg/vDcEjq6?">Discord</a> or the <a href="https://forum.adonisjs.com/">Forum</a>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
110
resources/views/welcome.edge
Normal file
110
resources/views/welcome.edge
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AdonisJS - A fully featured web framework for Node.js</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Poppins:400,500&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
background-color: #F7F8FA;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
color: #46444c;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:before {
|
||||||
|
content: '';
|
||||||
|
background: #5A45FF;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 6px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5A45FF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 620px;
|
||||||
|
margin: auto;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 0 30px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #17161A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 26px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main li:before {
|
||||||
|
content: '—';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main code {
|
||||||
|
font-size: 16px;
|
||||||
|
background: #e6e2ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div>
|
||||||
|
<h1 class="title"> It Works! </h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
Congratulations, you have just created your first AdonisJS app.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
The route for this page is defined inside <code>start/routes.ts</code> file
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
You can update this page by editing <code>resources/views/welcome.edge</code> file
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
If you run into problems, you can reach us on <a href="https://discord.gg/vDcEjq6?">Discord</a> or the <a href="https://forum.adonisjs.com/">Forum</a>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
21
server.ts
Normal file
21
server.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| AdonisJs Server
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The contents in this file is meant to bootstrap the AdonisJs application
|
||||||
|
| and start the HTTP server to accept incoming connections. You must avoid
|
||||||
|
| making this file dirty and instead make use of `lifecycle hooks` provided
|
||||||
|
| by AdonisJs service providers for custom code.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'reflect-metadata'
|
||||||
|
import sourceMapSupport from 'source-map-support'
|
||||||
|
import { Ignitor } from '@adonisjs/core/build/standalone'
|
||||||
|
|
||||||
|
sourceMapSupport.install({ handleUncaughtExceptions: false })
|
||||||
|
|
||||||
|
new Ignitor(__dirname)
|
||||||
|
.httpServer()
|
||||||
|
.start()
|
||||||
44
start/kernel.ts
Normal file
44
start/kernel.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This file is used to define middleware for HTTP requests. You can register
|
||||||
|
| middleware as a `closure` or an IoC container binding. The bindings are
|
||||||
|
| preferred, since they keep this file clean.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Server from '@ioc:Adonis/Core/Server'
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Global middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| An array of global middleware, that will be executed in the order they
|
||||||
|
| are defined for every HTTP requests.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
Server.middleware.register([
|
||||||
|
() => import('@ioc:Adonis/Core/BodyParser'),
|
||||||
|
])
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Named middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Named middleware are defined as key-value pair. The value is the namespace
|
||||||
|
| or middleware function and key is the alias. Later you can use these
|
||||||
|
| alias on individual routes. For example:
|
||||||
|
|
|
||||||
|
| { auth: () => import('App/Middleware/Auth') }
|
||||||
|
|
|
||||||
|
| and then use it as follows
|
||||||
|
|
|
||||||
|
| Route.get('dashboard', 'UserController.dashboard').middleware('auth')
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
Server.middleware.registerNamed({
|
||||||
|
})
|
||||||
59
start/routes.ts
Normal file
59
start/routes.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Routes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This file is dedicated for defining HTTP routes. A single file is enough
|
||||||
|
| for majority of projects, however you can define routes in different
|
||||||
|
| files and just make sure to import them inside this file. For example
|
||||||
|
|
|
||||||
|
| Define routes in following two files
|
||||||
|
| ├── start/routes/cart.ts
|
||||||
|
| ├── start/routes/customer.ts
|
||||||
|
|
|
||||||
|
| and then import them inside `start/routes.ts` as follows
|
||||||
|
|
|
||||||
|
| import './routes/cart'
|
||||||
|
| import './routes/customer''
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Route from '@ioc:Adonis/Core/Route'
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
Route.group(function () {
|
||||||
|
if (fs.existsSync(`${__dirname}/routes`)) {
|
||||||
|
const folders = fs.readdirSync(`${__dirname}/routes`)
|
||||||
|
folders.map((folder) => {
|
||||||
|
if (folder != 'auth') {
|
||||||
|
const files = fs.readdirSync(`${__dirname}/routes/${folder}`)
|
||||||
|
files.map((file) => {
|
||||||
|
if (!file.includes('.map')) {
|
||||||
|
require(`${__dirname}/routes/${folder}/${file}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).prefix('api')
|
||||||
|
|
||||||
|
Route.group(function () {
|
||||||
|
if (fs.existsSync(`${__dirname}/routes/auth`)) {
|
||||||
|
const files = fs.readdirSync(`${__dirname}/routes/auth`)
|
||||||
|
files.map((file) => {
|
||||||
|
if (!file.includes('.map')) {
|
||||||
|
require(`${__dirname}/routes/auth/${file}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).prefix('auth')
|
||||||
|
|
||||||
|
Route.get('/', async ({ view }) => {
|
||||||
|
return view.render('welcome')
|
||||||
|
})
|
||||||
|
|
||||||
|
Route.get('/api', async ({ response }) => {
|
||||||
|
return response.api(null, 'It works!')
|
||||||
|
})
|
||||||
|
|
||||||
|
Route.on('*').render('index')
|
||||||
8
start/routes/auth/auth.ts
Normal file
8
start/routes/auth/auth.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Route from '@ioc:Adonis/Core/Route'
|
||||||
|
|
||||||
|
Route.post('/login', 'Auth/AuthController.login').as('auth.login')
|
||||||
|
Route.post('/logout', 'Auth/AuthController.logout').as('auth.logout')
|
||||||
|
Route.get('/google/redirect', 'Auth/AuthController.oauthRedirect').as('auth.redirect')
|
||||||
|
Route.get('/google/callback', 'Auth/AuthController.oauthCallback').as('auth.callback')
|
||||||
|
Route.post('/forgot-password', 'Auth/AuthController.forgotPassword').as('auth.forgot-password')
|
||||||
|
Route.post('/reset-password', 'Auth/AuthController.restorePassword').as('auth.reset-password')
|
||||||
6
start/routes/user/roles.ts
Normal file
6
start/routes/user/roles.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import Route from '@ioc:Adonis/Core/Route'
|
||||||
|
|
||||||
|
Route.group(function () {
|
||||||
|
Route.delete('/', 'User/RoleController.destroyAll').as('roles.destroyAll')
|
||||||
|
}).prefix('roles')
|
||||||
|
Route.resource('roles', 'User/RoleController').apiOnly()
|
||||||
6
start/routes/user/users.ts
Normal file
6
start/routes/user/users.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import Route from '@ioc:Adonis/Core/Route'
|
||||||
|
|
||||||
|
Route.group(function () {
|
||||||
|
Route.delete('/', 'User/AccountController.destroyAll').as('users.destroyAll')
|
||||||
|
}).prefix('users')
|
||||||
|
Route.resource('users', 'User/AccountController').apiOnly()
|
||||||
40
tsconfig.json
Normal file
40
tsconfig.json
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/adonis-preset-ts/tsconfig",
|
||||||
|
"include": [
|
||||||
|
"**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"build"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "build",
|
||||||
|
"rootDir": "./",
|
||||||
|
"sourceMap": true,
|
||||||
|
"paths": {
|
||||||
|
"App/*": [
|
||||||
|
"./app/*"
|
||||||
|
],
|
||||||
|
"Config/*": [
|
||||||
|
"./config/*"
|
||||||
|
],
|
||||||
|
"Contracts/*": [
|
||||||
|
"./contracts/*"
|
||||||
|
],
|
||||||
|
"Database/*": [
|
||||||
|
"./database/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"types": [
|
||||||
|
"@adonisjs/core",
|
||||||
|
"@adonisjs/repl",
|
||||||
|
"@adonisjs/session",
|
||||||
|
"@adonisjs/view",
|
||||||
|
"@adonisjs/shield",
|
||||||
|
"@adonisjs/lucid",
|
||||||
|
"@adonisjs/auth",
|
||||||
|
"@adonisjs/mail",
|
||||||
|
"@adonisjs/ally"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user