mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
seeders, migrations & roles
This commit is contained in:
parent
bb12cc9bd4
commit
05fc06e744
@ -20,7 +20,9 @@
|
||||
"providers": [
|
||||
"./providers/AppProvider",
|
||||
"@adonisjs/core",
|
||||
"@adonisjs/lucid"
|
||||
"@adonisjs/lucid",
|
||||
"@adonisjs/auth",
|
||||
"@adonisjs/redis"
|
||||
],
|
||||
"aceProviders": [
|
||||
"@adonisjs/repl"
|
||||
|
||||
@ -8,3 +8,7 @@ PG_PORT=5432
|
||||
PG_USER=lucid
|
||||
PG_PASSWORD=
|
||||
PG_DB_NAME=lucid
|
||||
REDIS_CONNECTION=local
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
@ -4,5 +4,7 @@
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"jsxSingleQuote": true,
|
||||
"arrowParens": "avoid"
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "auto",
|
||||
"printWidth": 100
|
||||
}
|
||||
|
||||
55
app/Controllers/Http/AuthController.ts
Normal file
55
app/Controllers/Http/AuthController.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
import Logger from '@ioc:Adonis/Core/Logger';
|
||||
import Hash from '@ioc:Adonis/Core/Hash';
|
||||
import User from 'App/Models/User';
|
||||
import { Exception } from '@adonisjs/core/build/standalone';
|
||||
|
||||
export default class AuthController {
|
||||
private user: typeof User;
|
||||
|
||||
private guard: 'api';
|
||||
|
||||
constructor() {
|
||||
this.user = User;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new token for user and returns it.
|
||||
* POST /login
|
||||
*
|
||||
* @param {HttpContextContract} context
|
||||
* @returns {object} - Token data
|
||||
*/
|
||||
|
||||
public async login({ auth, request }: HttpContextContract) {
|
||||
const login = request.input('login');
|
||||
const password = request.input('password');
|
||||
const user = await this.user.query().preload('roles').where('login', login).firstOrFail();
|
||||
|
||||
if (!(await Hash.verify(user.password, password))) {
|
||||
throw new Exception('Invalid credentials.', 401, 'E_INVALID_CREDENTIALS');
|
||||
}
|
||||
|
||||
const token = await auth.use(this.guard).generate(user, {
|
||||
expiresIn: '1d',
|
||||
userRoles: user.roles.map(role => role.slug),
|
||||
});
|
||||
|
||||
Logger.info(`A token for user with id: "${token.user.id}" was successfully generated.`);
|
||||
|
||||
return token.toJSON();
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a token if user logged in.
|
||||
* POST /logout
|
||||
*
|
||||
* @param {HttpContextContract} context
|
||||
* @returns {object} - Revoked message
|
||||
*/
|
||||
|
||||
public async logout({ auth }: HttpContextContract) {
|
||||
await auth.use(this.guard).revoke();
|
||||
return { message: 'REVOKED' };
|
||||
}
|
||||
}
|
||||
4
app/Controllers/Http/ContactsController.ts
Normal file
4
app/Controllers/Http/ContactsController.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||
|
||||
export default class ContactsController {
|
||||
}
|
||||
83
app/Controllers/Http/RolesController.ts
Normal file
83
app/Controllers/Http/RolesController.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { Exception } from '@adonisjs/core/build/standalone';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
import Role from 'App/Models/Role';
|
||||
import User from 'App/Models/User';
|
||||
import AddRoleToUserValidator from 'App/Validators/AddRoleToUserValidator';
|
||||
|
||||
export default class RolesController {
|
||||
private readonly user: typeof User;
|
||||
|
||||
private readonly role: typeof Role;
|
||||
|
||||
constructor() {
|
||||
this.user = User;
|
||||
this.role = Role;
|
||||
}
|
||||
|
||||
private async findRolesBySlug(input: any) {
|
||||
const roles = await this.role.query().whereIn('slug', input);
|
||||
|
||||
if (roles.length !== input.length) {
|
||||
throw new Exception(
|
||||
'Unable to find any role using input value "roles"',
|
||||
404,
|
||||
'E_ROLE_NOT_FOUND'
|
||||
);
|
||||
}
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a role to a user by "id"
|
||||
* POST /user/:id/roles
|
||||
*
|
||||
* @param {HttpContextContract} context
|
||||
* @returns {Promise<User>} - Updated user.
|
||||
*/
|
||||
public async store({ request, params }: HttpContextContract): Promise<Role[]> {
|
||||
try {
|
||||
await request.validate(AddRoleToUserValidator);
|
||||
|
||||
const input = request.input('roles');
|
||||
const user = await this.user.findOrFail(params.id);
|
||||
const roles = await this.findRolesBySlug(input);
|
||||
|
||||
await user.related('roles').attach(roles.map(r => r.id));
|
||||
await user.load('roles');
|
||||
|
||||
return user.roles;
|
||||
} catch (error) {
|
||||
if (error.code === '23505') {
|
||||
throw new Exception('Some role already attached to that user.', 400, 'E_ROLE_ATTACHED');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach role from user by "id"
|
||||
* DELETE /users/:id/roles
|
||||
*
|
||||
*/
|
||||
public async destroy({ request, params }: HttpContextContract): Promise<Role[]> {
|
||||
const input = request.input('roles');
|
||||
const user = await this.user.query().preload('roles').where('id', params.id).firstOrFail();
|
||||
|
||||
/**
|
||||
* Detach all when "roles" input not provided
|
||||
*/
|
||||
if (input === undefined) {
|
||||
user.related('roles').detach([]);
|
||||
return [];
|
||||
}
|
||||
|
||||
const roles = await this.findRolesBySlug(input);
|
||||
|
||||
user.related('roles').detach(roles.map(r => r.id));
|
||||
await user.load('roles');
|
||||
|
||||
return user.roles;
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,21 @@
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
import Role from 'App/Models/Role';
|
||||
import User from 'App/Models/User';
|
||||
import Contact from 'App/Models/Contact';
|
||||
import CreateUserValidator from 'App/Validators/CreateUserValidator';
|
||||
import UpdateUserValidator from 'App/Validators/UpdateUserValidator';
|
||||
|
||||
export default class UsersController {
|
||||
private readonly userModel: typeof User;
|
||||
private readonly user: typeof User;
|
||||
|
||||
private readonly role: typeof Role;
|
||||
|
||||
private readonly contact: typeof Contact;
|
||||
|
||||
constructor() {
|
||||
this.userModel = User;
|
||||
this.user = User;
|
||||
this.role = Role;
|
||||
this.contact = Contact;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18,7 +26,7 @@ export default class UsersController {
|
||||
*/
|
||||
|
||||
public async index(): Promise<User[]> {
|
||||
const users = this.userModel.all();
|
||||
const users = await this.user.query().preload('contacts').preload('roles');
|
||||
return users;
|
||||
}
|
||||
|
||||
@ -31,7 +39,11 @@ export default class UsersController {
|
||||
*/
|
||||
|
||||
public async show({ params }: HttpContextContract): Promise<User> {
|
||||
const user = await this.userModel.findOrFail(params.user_id);
|
||||
const user = await this.user.findOrFail(params.id);
|
||||
|
||||
await user.load('contacts');
|
||||
await user.load('roles');
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@ -45,21 +57,27 @@ export default class UsersController {
|
||||
|
||||
public async store({ request }: HttpContextContract): Promise<User> {
|
||||
await request.validate(CreateUserValidator);
|
||||
const user = await this.userModel.create({
|
||||
name: request.input('name'),
|
||||
surname: request.input('surname'),
|
||||
patronymic: request.input('patronymic'),
|
||||
|
||||
const roles = await this.role.query().whereIn('slug', request.input('roles'));
|
||||
const user = await this.user.create({
|
||||
first_name: request.input('first_name'),
|
||||
last_name: request.input('last_name'),
|
||||
login: request.input('login'),
|
||||
email: request.input('email'),
|
||||
password: request.input('password'),
|
||||
});
|
||||
|
||||
await user.related('contacts').create({ email: request.input('email') });
|
||||
await user.related('roles').attach(roles.map(r => r.id));
|
||||
|
||||
await user.load('roles');
|
||||
await user.load('contacts');
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user in a system by "id".
|
||||
* PUT /users
|
||||
* PATCH /users/:id
|
||||
*
|
||||
* @param {HttpContextContract} context
|
||||
* @returns {Promise<User>}
|
||||
@ -67,14 +85,35 @@ export default class UsersController {
|
||||
|
||||
public async update({ request, params }: HttpContextContract): Promise<User> {
|
||||
await request.validate(UpdateUserValidator);
|
||||
const user = await this.userModel.findOrFail(params.id);
|
||||
user.name = request.input('name');
|
||||
user.surname = request.input('surname');
|
||||
user.patronymic = request.input('patronymic');
|
||||
|
||||
/**
|
||||
* User update
|
||||
*/
|
||||
const user = await this.user.findOrFail(params.id);
|
||||
user.first_name = request.input('first_name');
|
||||
user.last_name = request.input('last_name');
|
||||
user.login = request.input('login');
|
||||
user.email = request.input('email');
|
||||
user.password = request.input('password');
|
||||
|
||||
// await user.load('roles');
|
||||
|
||||
/**
|
||||
* Contacts update
|
||||
*/
|
||||
const contacts = await this.contact.findByOrFail('user_id', params.id);
|
||||
contacts.email = request.input('email');
|
||||
|
||||
/**
|
||||
* Save the updated data
|
||||
*/
|
||||
await user.save();
|
||||
await contacts.save();
|
||||
|
||||
/**
|
||||
* Load the updated data to return
|
||||
*/
|
||||
await user.load('roles');
|
||||
await user.load('contacts');
|
||||
|
||||
return user;
|
||||
}
|
||||
@ -88,7 +127,7 @@ export default class UsersController {
|
||||
*/
|
||||
|
||||
public async destroy({ params }: HttpContextContract): Promise<User> {
|
||||
const user = await this.userModel.findOrFail(params.id);
|
||||
const user = await this.user.findOrFail(params.id);
|
||||
await user.delete();
|
||||
|
||||
return user;
|
||||
|
||||
68
app/Middleware/Auth.ts
Normal file
68
app/Middleware/Auth.ts
Normal file
@ -0,0 +1,68 @@
|
||||
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;
|
||||
const authPromises: Promise<boolean>[] = guards.map(guard => auth.use(guard).check());
|
||||
const authResults: boolean[] = await Promise.all(authPromises);
|
||||
|
||||
for (let i = 0; i < authResults.length; i += 1) {
|
||||
/**
|
||||
* User has been successfully authenticated
|
||||
*/
|
||||
if (authResults[i]) return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unable to authenticate using any guard
|
||||
*/
|
||||
throw new AuthenticationException(
|
||||
'Unauthorized access',
|
||||
'E_UNAUTHORIZED_ACCESS',
|
||||
guardLastAttempted,
|
||||
this.redirectTo
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
40
app/Middleware/Role.ts
Normal file
40
app/Middleware/Role.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Exception } from '@adonisjs/core/build/standalone';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
export default class Role {
|
||||
error: {
|
||||
message: string;
|
||||
status: number;
|
||||
errorCode: string;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.error = {
|
||||
message: 'You dont have permission to perform that action.',
|
||||
status: 403,
|
||||
errorCode: 'E_ACCESS_DENIED',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an array of roles for the current user
|
||||
* If the user role is not found in the array of roles for the route, an error is thrown
|
||||
*
|
||||
* @param {string[]} roles - Array of roles for route
|
||||
* @param {string[]} userRole - Array of roles of current user
|
||||
*/
|
||||
protected checkRoles(roles: string[], userRoles: string[]): void {
|
||||
if (!roles.some(role => userRoles.includes(role))) {
|
||||
/**
|
||||
* Failed to verify user rights
|
||||
*/
|
||||
throw new Exception(this.error.message, this.error.status, this.error.errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public async handle({ auth }: HttpContextContract, next: () => Promise<void>, roles: string[]) {
|
||||
const { userRoles } = auth.use('api').token?.meta;
|
||||
this.checkRoles(roles, userRoles);
|
||||
await next();
|
||||
}
|
||||
}
|
||||
31
app/Models/Contact.ts
Normal file
31
app/Models/Contact.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
|
||||
export default class Contact extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
public id: number;
|
||||
|
||||
@column({ serializeAs: null })
|
||||
user_id: string;
|
||||
|
||||
@column()
|
||||
public email: string;
|
||||
|
||||
@column()
|
||||
public phone_number: string;
|
||||
|
||||
@column()
|
||||
public vk_id: string;
|
||||
|
||||
@column()
|
||||
public twitter_id: string;
|
||||
|
||||
@column()
|
||||
public telegram_id: string;
|
||||
|
||||
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||
public createdAt: DateTime;
|
||||
|
||||
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
|
||||
public updatedAt: DateTime;
|
||||
}
|
||||
20
app/Models/Role.ts
Normal file
20
app/Models/Role.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/* eslint-disable import/no-cycle */
|
||||
import { DateTime } from 'luxon';
|
||||
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
|
||||
export default class Role extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
public id: number;
|
||||
|
||||
@column()
|
||||
public name: string;
|
||||
|
||||
@column()
|
||||
public slug: string;
|
||||
|
||||
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||
public createdAt: DateTime;
|
||||
|
||||
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
|
||||
public updatedAt: DateTime;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable import/no-cycle */
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { DateTime } from 'luxon';
|
||||
import {
|
||||
@ -5,36 +6,46 @@ import {
|
||||
beforeCreate,
|
||||
beforeSave,
|
||||
column,
|
||||
HasOne,
|
||||
hasOne,
|
||||
ManyToMany,
|
||||
manyToMany,
|
||||
} from '@ioc:Adonis/Lucid/Orm';
|
||||
import Hash from '@ioc:Adonis/Core/Hash';
|
||||
import { nanoid } from 'nanoid';
|
||||
import Role from './Role';
|
||||
import Contact from './Contact';
|
||||
|
||||
export default class User extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
public id: string;
|
||||
|
||||
@column()
|
||||
public name: string;
|
||||
public first_name: string;
|
||||
|
||||
@column()
|
||||
public surname: string;
|
||||
public last_name: string;
|
||||
|
||||
@column()
|
||||
public patronymic: string;
|
||||
|
||||
@column()
|
||||
@column({ serializeAs: null })
|
||||
public login: string;
|
||||
|
||||
@column()
|
||||
public email: string;
|
||||
|
||||
@column({ serializeAs: null })
|
||||
public password: string;
|
||||
|
||||
@column.dateTime({ autoCreate: true })
|
||||
@hasOne(() => Contact, {
|
||||
foreignKey: 'user_id',
|
||||
})
|
||||
public contacts: HasOne<typeof Contact>;
|
||||
|
||||
@manyToMany(() => Role, {
|
||||
pivotTable: 'users_roles',
|
||||
})
|
||||
public roles: ManyToMany<typeof Role>;
|
||||
|
||||
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||
public createdAt: DateTime;
|
||||
|
||||
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
||||
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
|
||||
public updatedAt: DateTime;
|
||||
|
||||
@beforeCreate()
|
||||
|
||||
12
app/Validators/AddRoleToUserValidator.ts
Normal file
12
app/Validators/AddRoleToUserValidator.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { schema } from '@ioc:Adonis/Core/Validator';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
export default class AddRoleToUserValidator {
|
||||
constructor(protected ctx: HttpContextContract) {}
|
||||
|
||||
public schema = schema.create({
|
||||
roles: schema.array().members(schema.string()),
|
||||
});
|
||||
|
||||
public messages = {};
|
||||
}
|
||||
@ -24,14 +24,13 @@ export default class CreateUserValidator {
|
||||
* ```
|
||||
*/
|
||||
public schema = schema.create({
|
||||
name: schema.string(),
|
||||
surname: schema.string(),
|
||||
patronymic: schema.string(),
|
||||
login: schema.string(),
|
||||
email: schema.string({}, [
|
||||
rules.email(),
|
||||
rules.unique({ table: 'users', column: 'email' }),
|
||||
first_name: schema.string(),
|
||||
last_name: schema.string(),
|
||||
login: schema.string({}, [
|
||||
rules.maxLength(128),
|
||||
rules.unique({ table: 'users', column: 'login' }),
|
||||
]),
|
||||
email: schema.string({}, [rules.email(), rules.unique({ table: 'contacts', column: 'email' })]),
|
||||
password: schema.string(),
|
||||
});
|
||||
|
||||
|
||||
@ -5,27 +5,15 @@ export default class UpdateUserValidator {
|
||||
constructor(protected ctx: HttpContextContract) {}
|
||||
|
||||
public schema = schema.create({
|
||||
name: schema.string.optional(),
|
||||
surname: schema.string.optional(),
|
||||
patronymic: schema.string.optional(),
|
||||
login: schema.string.optional(),
|
||||
first_name: schema.string.optional(),
|
||||
last_name: schema.string.optional(),
|
||||
login: schema.string.optional({}, [rules.unique({ table: 'users', column: 'login' })]),
|
||||
email: schema.string.optional({}, [
|
||||
rules.email(),
|
||||
rules.unique({ table: 'users', column: 'email' }),
|
||||
rules.unique({ table: 'contacts', column: 'email' }),
|
||||
]),
|
||||
password: schema.string.optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* 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 = {};
|
||||
}
|
||||
|
||||
112
config/auth.ts
Normal file
112
config/auth.ts
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis provider for managing tokens
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Uses Redis for managing tokens. We recommend using the "redis" driver
|
||||
| over the "database" driver when the tokens based auth is the
|
||||
| primary authentication mode.
|
||||
|
|
||||
| Redis ensure that all the expired tokens gets cleaned up automatically.
|
||||
| Whereas with SQL, you have to cleanup expired tokens manually.
|
||||
|
|
||||
| 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: 'redis',
|
||||
redisConnection: 'local',
|
||||
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'),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default authConfig;
|
||||
@ -44,6 +44,10 @@ const databaseConfig: DatabaseConfig = {
|
||||
},
|
||||
migrations: {
|
||||
naturalSort: true,
|
||||
tableName: 'migrations',
|
||||
},
|
||||
seeders: {
|
||||
paths: ['./database/seeders/MainSeeder'],
|
||||
},
|
||||
healthCheck: true,
|
||||
debug: true,
|
||||
|
||||
48
config/redis.ts
Normal file
48
config/redis.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Config source: https://git.io/JemcF
|
||||
*
|
||||
* 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 { RedisConfig } from '@ioc:Adonis/Addons/Redis';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Following is the configuration used by the Redis provider to connect to
|
||||
| the redis server and execute redis commands.
|
||||
|
|
||||
| Do make sure to pre-define the connections type inside `contracts/redis.ts`
|
||||
| file for AdonisJs to recognize connections.
|
||||
|
|
||||
| Make sure to check `contracts/redis.ts` file for defining extra connections
|
||||
*/
|
||||
const redisConfig: RedisConfig = {
|
||||
connection: Env.get('REDIS_CONNECTION'),
|
||||
|
||||
connections: {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| The default connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The main connection you want to use to execute redis commands. The same
|
||||
| connection will be used by the session provider, if you rely on the
|
||||
| redis driver.
|
||||
|
|
||||
*/
|
||||
local: {
|
||||
host: Env.get('REDIS_HOST'),
|
||||
port: Env.get('REDIS_PORT'),
|
||||
password: Env.get('REDIS_PASSWORD', ''),
|
||||
db: 0,
|
||||
keyPrefix: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default redisConfig;
|
||||
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 User from 'App/Models/User'
|
||||
|
||||
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 User>,
|
||||
config: LucidProviderConfig<typeof User>,
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 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'>,
|
||||
},
|
||||
}
|
||||
}
|
||||
12
contracts/redis.ts
Normal file
12
contracts/redis.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Contract source: https://git.io/JemcN
|
||||
*
|
||||
* Feel free to let us know via PR, if you find something broken in this config
|
||||
* file.
|
||||
*/
|
||||
|
||||
declare module '@ioc:Adonis/Addons/Redis' {
|
||||
interface RedisConnectionsList {
|
||||
local: RedisConnectionConfig;
|
||||
}
|
||||
}
|
||||
@ -5,12 +5,10 @@ export default class Users extends BaseSchema {
|
||||
|
||||
public async up() {
|
||||
this.schema.createTable(this.tableName, table => {
|
||||
table.string('id', 21).primary().notNullable();
|
||||
table.string('name', 24).notNullable();
|
||||
table.string('surname', 24).notNullable();
|
||||
table.string('patronymic', 24).notNullable();
|
||||
table.string('id', 21).primary();
|
||||
table.string('first_name', 24).notNullable();
|
||||
table.string('last_name', 24).notNullable();
|
||||
table.string('login', 128).notNullable().unique();
|
||||
table.string('email', 254).notNullable().unique();
|
||||
table.string('password').notNullable();
|
||||
table.timestamp('created_at', { useTz: true });
|
||||
table.timestamp('updated_at', { useTz: true });
|
||||
19
database/migrations/1626964029348_roles.ts
Normal file
19
database/migrations/1626964029348_roles.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
|
||||
|
||||
export default class Roles extends BaseSchema {
|
||||
protected tableName = 'roles';
|
||||
|
||||
public async up() {
|
||||
this.schema.createTable(this.tableName, table => {
|
||||
table.increments('id');
|
||||
table.string('name');
|
||||
table.string('slug');
|
||||
table.timestamp('created_at', { useTz: true });
|
||||
table.timestamp('updated_at', { useTz: true });
|
||||
});
|
||||
}
|
||||
|
||||
public async down() {
|
||||
this.schema.dropTable(this.tableName);
|
||||
}
|
||||
}
|
||||
18
database/migrations/1626970448054_users_roles.ts
Normal file
18
database/migrations/1626970448054_users_roles.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
|
||||
|
||||
export default class UsersRoles extends BaseSchema {
|
||||
protected tableName = 'users_roles';
|
||||
|
||||
public async up() {
|
||||
this.schema.createTable(this.tableName, table => {
|
||||
table.increments('id');
|
||||
table.string('user_id', 21).unsigned().references('users.id').onDelete('CASCADE');
|
||||
table.integer('role_id').unsigned().references('roles.id').onDelete('CASCADE');
|
||||
table.unique(['user_id', 'role_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public async down() {
|
||||
this.schema.dropTable(this.tableName);
|
||||
}
|
||||
}
|
||||
23
database/migrations/1627022639849_contacts.ts
Normal file
23
database/migrations/1627022639849_contacts.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
|
||||
|
||||
export default class Contacts extends BaseSchema {
|
||||
protected tableName = 'contacts';
|
||||
|
||||
public async up() {
|
||||
this.schema.createTable(this.tableName, table => {
|
||||
table.increments('id');
|
||||
table.string('user_id', 21).unsigned().references('users.id').onDelete('CASCADE');
|
||||
table.string('email').unique().notNullable();
|
||||
table.string('phone_number').unique();
|
||||
table.string('vk_id').unique();
|
||||
table.string('twitter_id').unique();
|
||||
table.string('telegram_id').unique();
|
||||
table.timestamp('created_at', { useTz: true });
|
||||
table.timestamp('updated_at', { useTz: true });
|
||||
});
|
||||
}
|
||||
|
||||
public async down() {
|
||||
this.schema.dropTable(this.tableName);
|
||||
}
|
||||
}
|
||||
23
database/seeders/MainSeeder/Index.ts
Normal file
23
database/seeders/MainSeeder/Index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
||||
import Application from '@ioc:Adonis/Core/Application';
|
||||
|
||||
export default class IndexSeeder extends BaseSeeder {
|
||||
private async runSeeder(seeder: { default: typeof BaseSeeder }) {
|
||||
const Seeder = seeder.default;
|
||||
|
||||
/**
|
||||
* Do not run when not in dev mode and seeder is development
|
||||
* only
|
||||
*/
|
||||
if (Seeder.developmentOnly && !Application.inDev) {
|
||||
return;
|
||||
}
|
||||
|
||||
await new Seeder(this.client).run();
|
||||
}
|
||||
|
||||
public async run() {
|
||||
await this.runSeeder(await import('../Role'));
|
||||
await this.runSeeder(await import('../User'));
|
||||
}
|
||||
}
|
||||
22
database/seeders/Role.ts
Normal file
22
database/seeders/Role.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
||||
import Role from 'App/Models/Role';
|
||||
|
||||
export default class RoleSeeder extends BaseSeeder {
|
||||
public async run() {
|
||||
await Role.createMany([
|
||||
{
|
||||
name: 'Administrator',
|
||||
slug: 'admin',
|
||||
},
|
||||
{
|
||||
name: 'Teacher',
|
||||
slug: 'teacher',
|
||||
},
|
||||
{
|
||||
name: 'Student',
|
||||
slug: 'student',
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
56
database/seeders/User.ts
Normal file
56
database/seeders/User.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
||||
import Role from 'App/Models/Role';
|
||||
import User from 'App/Models/User';
|
||||
|
||||
export default class UserSeeder extends BaseSeeder {
|
||||
public async run() {
|
||||
/**
|
||||
* Roles
|
||||
*/
|
||||
const roles = {
|
||||
admin: await Role.findByOrFail('slug', 'admin'),
|
||||
teacher: await Role.findByOrFail('slug', 'teacher'),
|
||||
student: await Role.findByOrFail('slug', 'student'),
|
||||
};
|
||||
|
||||
/**
|
||||
* Administrator
|
||||
*/
|
||||
const user1 = new User();
|
||||
user1.first_name = 'Ivan';
|
||||
user1.last_name = 'Ivanov';
|
||||
user1.login = 'ivan123';
|
||||
user1.password = 'ivan123';
|
||||
|
||||
await user1.save();
|
||||
await user1.related('roles').attach([roles.admin.id]);
|
||||
await user1.related('contacts').create({ email: 'ivan_ivanov@ya.ru' });
|
||||
|
||||
/**
|
||||
* Teacher
|
||||
*/
|
||||
const user2 = new User();
|
||||
user2.first_name = 'Nikolai';
|
||||
user2.last_name = 'Nikolaev';
|
||||
user2.login = 'nikolai123';
|
||||
user2.password = 'nikolai123';
|
||||
|
||||
await user2.save();
|
||||
await user2.related('roles').attach([roles.teacher.id]);
|
||||
await user2.related('contacts').create({ email: 'nikolai_niko123@ya.ru' });
|
||||
|
||||
/**
|
||||
* Student
|
||||
*/
|
||||
const user3 = new User();
|
||||
user3.first_name = 'Oleg';
|
||||
user3.last_name = 'Olegov';
|
||||
user3.login = 'oleg123';
|
||||
user3.password = 'oleg123';
|
||||
|
||||
await user3.save();
|
||||
await user3.related('roles').attach([roles.student.id]);
|
||||
await user3.related('contacts').create({ email: 'oleg_olegov123@ya.ru' });
|
||||
}
|
||||
}
|
||||
4
env.ts
4
env.ts
@ -25,4 +25,8 @@ export default Env.rules({
|
||||
PG_USER: Env.schema.string(),
|
||||
PG_PASSWORD: Env.schema.string.optional(),
|
||||
PG_DB_NAME: Env.schema.string(),
|
||||
REDIS_CONNECTION: Env.schema.enum(['local'] as const),
|
||||
REDIS_HOST: Env.schema.string({ format: 'host' }),
|
||||
REDIS_PORT: Env.schema.number(),
|
||||
REDIS_PASSWORD: Env.schema.string.optional(),
|
||||
});
|
||||
|
||||
85
package-lock.json
generated
85
package-lock.json
generated
@ -237,6 +237,16 @@
|
||||
"jest-worker": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@adonisjs/redis": {
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@adonisjs/redis/-/redis-7.0.7.tgz",
|
||||
"integrity": "sha512-M9nn2uaE8IJ25mMkoPQtrsvfjewjZSfOr2F2RzySDq/ymoOQ5RcBDrgZ40E/JUqPqDOV5zmU2E0ClEwujQFPmg==",
|
||||
"requires": {
|
||||
"@poppinss/utils": "^3.1.3",
|
||||
"@types/ioredis": "^4.26.4",
|
||||
"ioredis": "^4.27.6"
|
||||
}
|
||||
},
|
||||
"@adonisjs/repl": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@adonisjs/repl/-/repl-3.1.4.tgz",
|
||||
@ -668,6 +678,14 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/he/-/he-1.1.2.tgz",
|
||||
"integrity": "sha512-kSJPcLO1x+oolc0R89pUl2kozldQ/fVQ1C1p5mp8fPoLdF/ZcBvckaTC2M8xXh3GYendXvCpy5m/a2eSbfgNgw=="
|
||||
},
|
||||
"@types/ioredis": {
|
||||
"version": "4.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.26.5.tgz",
|
||||
"integrity": "sha512-aP/SUSFShzoVYcHHKKEq7+kQ5YaDsMRe64rcHCp+w4Unkfw2tico1vA6siPnMDe7+jIxuZRU/h8km6uZjEtswg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz",
|
||||
@ -1643,6 +1661,11 @@
|
||||
"string-width": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"cluster-key-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
|
||||
"integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw=="
|
||||
},
|
||||
"co-compose": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/co-compose/-/co-compose-6.1.3.tgz",
|
||||
@ -1938,6 +1961,11 @@
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||
},
|
||||
"denque": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
|
||||
"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
@ -3381,6 +3409,30 @@
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
|
||||
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
|
||||
},
|
||||
"ioredis": {
|
||||
"version": "4.27.6",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.6.tgz",
|
||||
"integrity": "sha512-6W3ZHMbpCa8ByMyC1LJGOi7P2WiOKP9B3resoZOVLDhi+6dDBOW+KNsRq3yI36Hmnb2sifCxHX+YSarTeXh48A==",
|
||||
"requires": {
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.1",
|
||||
"denque": "^1.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"p-map": "^2.1.0",
|
||||
"redis-commands": "1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-map": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
|
||||
"integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
@ -3835,6 +3887,16 @@
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
|
||||
},
|
||||
"lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -5266,6 +5328,24 @@
|
||||
"esprima": "~4.0.0"
|
||||
}
|
||||
},
|
||||
"redis-commands": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
|
||||
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
|
||||
},
|
||||
"redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
|
||||
},
|
||||
"redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
|
||||
"requires": {
|
||||
"redis-errors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
@ -5824,6 +5904,11 @@
|
||||
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
|
||||
"dev": true
|
||||
},
|
||||
"standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
|
||||
},
|
||||
"static-extend": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
"@adonisjs/auth": "^8.0.7",
|
||||
"@adonisjs/core": "^5.1.9",
|
||||
"@adonisjs/lucid": "^15.0.3",
|
||||
"@adonisjs/redis": "^7.0.7",
|
||||
"@adonisjs/repl": "^3.1.4",
|
||||
"luxon": "^1.28.0",
|
||||
"nanoid": "^3.1.23",
|
||||
|
||||
@ -38,4 +38,7 @@ Server.middleware.register([() => import('@ioc:Adonis/Core/BodyParser')]);
|
||||
| Route.get('dashboard', 'UserController.dashboard').middleware('auth')
|
||||
|
|
||||
*/
|
||||
Server.middleware.registerNamed({});
|
||||
Server.middleware.registerNamed({
|
||||
auth: () => import('App/Middleware/Auth'),
|
||||
role: () => import('App/Middleware/Role'),
|
||||
});
|
||||
|
||||
@ -23,9 +23,18 @@ import Route from '@ioc:Adonis/Core/Route';
|
||||
Route.get('/', () => `API REST Server.`);
|
||||
|
||||
Route.group(() => {
|
||||
Route.resource('users', 'UsersController').apiOnly();
|
||||
// Route.get('/users', 'UsersController.index');
|
||||
// Route.get('/users/:id', 'UsersController.show');
|
||||
// Route.post('/users', 'UsersController.store');
|
||||
// Route.put('/users/:id', 'UsersController.update');
|
||||
/* Auth */
|
||||
Route.post('login', 'AuthController.login');
|
||||
Route.post('logout', 'AuthController.logout').middleware('auth');
|
||||
|
||||
/* API */
|
||||
Route.group(() => {
|
||||
Route.get('users', 'UsersController.index').middleware('role:admin,teacher,student');
|
||||
Route.get('users/:id', 'UsersController.show').middleware('role:admin,teacher,student');
|
||||
Route.post('users', 'UsersController.store').middleware('role:admin');
|
||||
Route.patch('users/:id', 'UsersController.update').middleware('role:admin');
|
||||
Route.delete('users', 'UsersController.destroy').middleware('role:admin');
|
||||
Route.post('users/:id/roles', 'RolesController.store').middleware('role:admin');
|
||||
Route.delete('users/:id/roles', 'RolesController.destroy').middleware('role:admin');
|
||||
}).middleware('auth');
|
||||
}).prefix('api');
|
||||
|
||||
@ -28,7 +28,9 @@
|
||||
"types": [
|
||||
"@adonisjs/core",
|
||||
"@adonisjs/repl",
|
||||
"@adonisjs/lucid"
|
||||
"@adonisjs/lucid",
|
||||
"@adonisjs/auth",
|
||||
"@adonisjs/redis"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user