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