Added settings API
This commit is contained in:
@@ -22,7 +22,7 @@ type ResultShow = {
|
|||||||
|
|
||||||
export default class EmployeesController {
|
export default class EmployeesController {
|
||||||
public async index ({bouncer, request}: HttpContextContract) {
|
public async index ({bouncer, request}: HttpContextContract) {
|
||||||
await bouncer.authorize('employees.index')
|
await bouncer.with('EmployeesPolicy').authorize('index')
|
||||||
|
|
||||||
const limit: number = request.qs().limit ?? 10
|
const limit: number = request.qs().limit ?? 10
|
||||||
const page: number = request.qs().page ?? 1
|
const page: number = request.qs().page ?? 1
|
||||||
@@ -46,7 +46,9 @@ export default class EmployeesController {
|
|||||||
return employees.paginate(page, limit)
|
return employees.paginate(page, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async store ({request}: HttpContextContract) {
|
public async store ({request, bouncer}: HttpContextContract) {
|
||||||
|
await bouncer.with('EmployeesPolicy').authorize('store')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await request.validate(CreateEmployeeValidator)
|
const payload = await request.validate(CreateEmployeeValidator)
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ export default class EmployeesController {
|
|||||||
emp = await Employee.findOrFail(params.id)
|
emp = await Employee.findOrFail(params.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
await bouncer.authorize('employees.show', emp)
|
await bouncer.with('EmployeesPolicy').authorize('show', emp)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: emp.id,
|
id: emp.id,
|
||||||
@@ -101,7 +103,7 @@ export default class EmployeesController {
|
|||||||
const employee : Employee = await Employee.findOrFail(params.id)
|
const employee : Employee = await Employee.findOrFail(params.id)
|
||||||
const editContractHours : boolean = employee.contractHours !== request.input('contractHours')
|
const editContractHours : boolean = employee.contractHours !== request.input('contractHours')
|
||||||
|
|
||||||
await bouncer.authorize('employees.update', editContractHours, employee)
|
await bouncer.with('EmployeesPolicy').authorize('update', editContractHours, employee)
|
||||||
|
|
||||||
const payload = await request.validate(UpdateEmployeeValidator)
|
const payload = await request.validate(UpdateEmployeeValidator)
|
||||||
|
|
||||||
@@ -131,7 +133,7 @@ export default class EmployeesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async destroy ({params, bouncer}: HttpContextContract) {
|
public async destroy ({params, bouncer}: HttpContextContract) {
|
||||||
await bouncer.authorize('employees.destroy')
|
await bouncer.with('EmployeesPolicy').authorize('destroy')
|
||||||
|
|
||||||
return await Database.from('employees').where('id', params.id).delete()
|
return await Database.from('employees').where('id', params.id).delete()
|
||||||
}
|
}
|
||||||
@@ -186,7 +188,7 @@ export default class EmployeesController {
|
|||||||
|
|
||||||
let arr = qs.split(',').filter(item => item !== 'password' && item !== '' && columns.hasOwnProperty(item))
|
let arr = qs.split(',').filter(item => item !== 'password' && item !== '' && columns.hasOwnProperty(item))
|
||||||
|
|
||||||
if(arr.length === 0) arr = ['id', 'last_name', 'first_name', 'email', 'mobile', 'phone', 'role']
|
if(arr.length === 0) arr = ['id', 'last_name', 'first_name', 'shorthand', 'email', 'mobile', 'phone', 'role']
|
||||||
|
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|||||||
82
app/Controllers/Http/SettingsController.ts
Normal file
82
app/Controllers/Http/SettingsController.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
import Employee from 'App/Models/Employee'
|
||||||
|
import SetSettingsValidator from 'App/Validators/SetSettingsValidator'
|
||||||
|
|
||||||
|
type ResultSetting = {
|
||||||
|
key: string,
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SettingsController {
|
||||||
|
|
||||||
|
public async list({ params, bouncer }: HttpContextContract ): Promise<ResultSetting[]> {
|
||||||
|
const userId = params.userId
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await Employee.findOrFail(userId)
|
||||||
|
|
||||||
|
await bouncer.with('SettingsPolicy').authorize('do', user)
|
||||||
|
|
||||||
|
const result = await user.related('settings').query().select(['key', 'value'])
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async get({params, bouncer}: HttpContextContract): Promise<ResultSetting | null> {
|
||||||
|
const userId = params.userId
|
||||||
|
const key = params.key
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await Employee.findOrFail(userId)
|
||||||
|
|
||||||
|
await bouncer.with('SettingsPolicy').authorize('do', user)
|
||||||
|
|
||||||
|
const result = user.related('settings').query().select(['key', 'value']).where('key', key).first()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async set({params, request, bouncer}: HttpContextContract): Promise<'ok'> {
|
||||||
|
const userId = params.userId
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await request.validate(SetSettingsValidator)
|
||||||
|
const user = await Employee.findOrFail(userId)
|
||||||
|
|
||||||
|
await bouncer.with('SettingsPolicy').authorize('do', user)
|
||||||
|
|
||||||
|
await user.related('settings').updateOrCreateMany(payload.settings, 'key')
|
||||||
|
|
||||||
|
return "ok"
|
||||||
|
}
|
||||||
|
catch(error){
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete({ params, bouncer }: HttpContextContract): Promise<(0 | 1)[]> {
|
||||||
|
const userId = params.userId
|
||||||
|
const key = params.key
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await Employee.findOrFail(userId)
|
||||||
|
await bouncer.with('SettingsPolicy').authorize('do', user)
|
||||||
|
|
||||||
|
return await user.related('settings').query().where('key', key).delete()
|
||||||
|
}
|
||||||
|
catch(error){
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
import { BaseModel, beforeSave, column } from '@ioc:Adonis/Lucid/Orm'
|
import { BaseModel, beforeSave, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
|
||||||
|
import Setting from 'App/Models/Setting'
|
||||||
import Hash from '@ioc:Adonis/Core/Hash'
|
import Hash from '@ioc:Adonis/Core/Hash'
|
||||||
|
|
||||||
export default class Employee extends BaseModel {
|
export default class Employee extends BaseModel {
|
||||||
@@ -45,10 +46,17 @@ export default class Employee extends BaseModel {
|
|||||||
@column({serializeAs: null})
|
@column({serializeAs: null})
|
||||||
public password: string
|
public password: string
|
||||||
|
|
||||||
|
@hasMany(() => Setting)
|
||||||
|
public settings: HasMany<typeof Setting>
|
||||||
|
|
||||||
@beforeSave()
|
@beforeSave()
|
||||||
public static async hashPassword(employee: Employee){
|
public static async hashPassword(employee: Employee){
|
||||||
if(employee.$dirty.password){
|
if(employee.$dirty.password){
|
||||||
employee.password = await Hash.make(employee.password)
|
employee.password = await Hash.make(employee.password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isAdmin(): boolean {
|
||||||
|
return this.role === 'admin'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
app/Models/Setting.ts
Normal file
26
app/Models/Setting.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { DateTime } from 'luxon'
|
||||||
|
import { BaseModel, belongsTo, BelongsTo, column } from '@ioc:Adonis/Lucid/Orm'
|
||||||
|
import Employee from 'App/Models/Employee'
|
||||||
|
|
||||||
|
export default class Setting extends BaseModel {
|
||||||
|
@column({ isPrimary: true })
|
||||||
|
public id: number
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true })
|
||||||
|
public createdAt: DateTime
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
||||||
|
public updatedAt: DateTime
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public employeeId: number
|
||||||
|
|
||||||
|
@belongsTo(() => Employee)
|
||||||
|
public employee: BelongsTo<typeof Employee>
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public key: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public value: string
|
||||||
|
}
|
||||||
24
app/Policies/EmployeesPolicy.ts
Normal file
24
app/Policies/EmployeesPolicy.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { BasePolicy } from '@ioc:Adonis/Addons/Bouncer'
|
||||||
|
import Employee from 'App/Models/Employee'
|
||||||
|
|
||||||
|
export default class EmployeesPolicy extends BasePolicy {
|
||||||
|
public async index(employee: Employee) {
|
||||||
|
return employee.isAdmin()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async show(employee: Employee, query: Employee) {
|
||||||
|
return employee.isAdmin() || employee.id === query.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public async store(employee: Employee) {
|
||||||
|
return employee.isAdmin()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async update(employee: Employee, editContractHours: boolean, query: Employee) {
|
||||||
|
return employee.isAdmin() || (employee.id === query.id && !editContractHours)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroy(employee: Employee) {
|
||||||
|
return employee.isAdmin()
|
||||||
|
}
|
||||||
|
}
|
||||||
8
app/Policies/SettingsPolicy.ts
Normal file
8
app/Policies/SettingsPolicy.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { BasePolicy } from '@ioc:Adonis/Addons/Bouncer'
|
||||||
|
import Employee from 'App/Models/Employee'
|
||||||
|
|
||||||
|
export default class SettingsPolicy extends BasePolicy {
|
||||||
|
public async do(user: Employee, query: Employee){
|
||||||
|
return user.isAdmin() || user.id === query.id
|
||||||
|
}
|
||||||
|
}
|
||||||
56
app/Validators/SetSettingsValidator.ts
Normal file
56
app/Validators/SetSettingsValidator.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { schema, rules } from '@ioc:Adonis/Core/Validator'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
export default class SetSettingsValidator {
|
||||||
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define schema to validate the "shape", "type", "formatting" and "integrity" of data.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* 1. The username must be of data type string. But then also, it should
|
||||||
|
* not contain special characters or numbers.
|
||||||
|
* ```
|
||||||
|
* schema.string({}, [ rules.alpha() ])
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 2. The email must be of data type string, formatted as a valid
|
||||||
|
* email. But also, not used by any other user.
|
||||||
|
* ```
|
||||||
|
* schema.string({}, [
|
||||||
|
* rules.email(),
|
||||||
|
* rules.unique({ table: 'users', column: 'email' }),
|
||||||
|
* ])
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public schema = schema.create({
|
||||||
|
settings: schema.array().members(
|
||||||
|
schema.object().members({
|
||||||
|
key: schema.string({
|
||||||
|
trim: true,
|
||||||
|
}, [
|
||||||
|
rules.alpha({
|
||||||
|
allow: ['dash', 'underscore']
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
|
||||||
|
value: schema.string({
|
||||||
|
trim: true
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom messages for validation failures. You can make use of dot notation `(.)`
|
||||||
|
* for targeting nested fields and array expressions `(*)` for targeting all
|
||||||
|
* children of an array. For example:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* 'profile.username.required': 'Username is required',
|
||||||
|
* 'scores.*.number': 'Define scores as valid numbers'
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public messages = {}
|
||||||
|
}
|
||||||
29
database/migrations/1636667124834_settings.ts
Normal file
29
database/migrations/1636667124834_settings.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
|
||||||
|
|
||||||
|
export default class Settings extends BaseSchema {
|
||||||
|
protected tableName = 'settings'
|
||||||
|
|
||||||
|
public async up () {
|
||||||
|
this.schema.createTable(this.tableName, (table) => {
|
||||||
|
table.increments('id')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
|
||||||
|
*/
|
||||||
|
table.timestamp('created_at', { useTz: true })
|
||||||
|
table.timestamp('updated_at', { useTz: true })
|
||||||
|
|
||||||
|
table.integer('employee_id')
|
||||||
|
.unsigned()
|
||||||
|
.references('employees.id')
|
||||||
|
.onDelete('CASCADE')
|
||||||
|
|
||||||
|
table.string('key')
|
||||||
|
table.string('value')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down () {
|
||||||
|
this.schema.dropTable(this.tableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Bouncer from '@ioc:Adonis/Addons/Bouncer'
|
import Bouncer from '@ioc:Adonis/Addons/Bouncer'
|
||||||
import Employee from 'App/Models/Employee'
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -32,37 +31,6 @@ import Employee from 'App/Models/Employee'
|
|||||||
*/
|
*/
|
||||||
export const { actions } = Bouncer
|
export const { actions } = Bouncer
|
||||||
|
|
||||||
.define('employees.index', (user: Employee) => {
|
|
||||||
if(user.role !== 'admin') return Bouncer.deny('You are not allowed to view all employees')
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
.define('employees.show', (user: Employee, query: Employee) => {
|
|
||||||
if(user.role !== 'admin' && user.id !== query.id){
|
|
||||||
return Bouncer.deny('You are not allowd to view employees other than yourself')
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
.define('employees.store', (user: Employee) => {
|
|
||||||
if(user.role !== 'admin') return Bouncer.deny('You are not allowd to create any employees')
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
.define('employees.destroy', (user: Employee) => {
|
|
||||||
if(user.role !== 'admin') return Bouncer.deny('You are not allowed to delete any employees')
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
.define('employees.update', (user: Employee, editContractHours : boolean, query: Employee) => {
|
|
||||||
if(user.id !== query.id && user.role !== 'admin'){
|
|
||||||
return Bouncer.deny('You are not allowed to edit employees other than yourself.')
|
|
||||||
} else if (editContractHours && user.role !== 'admin'){
|
|
||||||
return Bouncer.deny('You are not allowed to edit your contract hours.')
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Bouncer Policies
|
| Bouncer Policies
|
||||||
@@ -86,4 +54,7 @@ export const { actions } = Bouncer
|
|||||||
| NOTE: Always export the "policies" const from this file
|
| NOTE: Always export the "policies" const from this file
|
||||||
|****************************************************************
|
|****************************************************************
|
||||||
*/
|
*/
|
||||||
export const { policies } = Bouncer.registerPolicies({})
|
export const { policies } = Bouncer.registerPolicies({
|
||||||
|
EmployeesPolicy: () => import('App/Policies/EmployeesPolicy'),
|
||||||
|
SettingsPolicy: () => import('App/Policies/SettingsPolicy'),
|
||||||
|
})
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ Route.group(() => {
|
|||||||
Route.post('logout', 'AuthController.logout').as('logout')
|
Route.post('logout', 'AuthController.logout').as('logout')
|
||||||
|
|
||||||
Route.resource('employees', 'EmployeesController').apiOnly()
|
Route.resource('employees', 'EmployeesController').apiOnly()
|
||||||
|
|
||||||
|
Route.get('settings/:userId', 'SettingsController.list').as('settings.list')
|
||||||
|
Route.get('settings/:userId/:key', 'SettingsController.get').as('settings.get')
|
||||||
|
Route.post('settings/:userId', 'SettingsController.set').as('settings.set')
|
||||||
|
Route.delete('settings/:userId/:key', 'SettingsController.delete').as('settings.delete')
|
||||||
})
|
})
|
||||||
.prefix('api/v1')
|
.prefix('api/v1')
|
||||||
.middleware('auth')
|
.middleware('auth')
|
||||||
|
|||||||
Reference in New Issue
Block a user