Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/controllers/admin/admin_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { HttpContext } from '@adonisjs/core/http'

export default class AdminController {
async render({ inertia }: HttpContext) {
return inertia.render('admin/index')
}
}
15 changes: 15 additions & 0 deletions app/controllers/admin/shares/admin_list_shares_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { HttpContext } from '@adonisjs/core/http'
import Share from '#models/share'
import ShareListAdminDto from '../../../dtos/shares/share_admin_dto.js'

export default class AdminListSharesController {
async render({ inertia }: HttpContext) {
const sharesModels = await Share.query().preload('folder')

const shares = await Promise.all(
sharesModels.map((share) => ShareListAdminDto.fromModel(share))
)

return inertia.render('admin/shares/index', { shares })
}
}
14 changes: 14 additions & 0 deletions app/controllers/admin/users/admin_list_users_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// import type { HttpContext } from '@adonisjs/core/http'

import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
import UsersListingDto from '../../../dtos/users/users_listing_dto.js'

export default class AdminListUserController {
async render({ inertia }: HttpContext) {
const users = await User.all()
const dtos = await Promise.all(users.map((user) => UsersListingDto.fromModel(user)))

return inertia.render('admin/users/index', { users: dtos })
}
}
18 changes: 18 additions & 0 deletions app/controllers/admin/users/admin_remove_users_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { HttpContext } from '@adonisjs/core/http'
import User from '#models/user'

export default class AdminRemoveUsersController {
async remove({ request, response, session }: HttpContext) {
const user = await User.findOrFail(request.param('id'))

if (user.email === 'admin@knowledge.fr') {
session.flash('error', 'You can not remove admin user')
return response.redirect().back()
}

await user.delete()

session.flash('success', 'User remove with successfully')
return response.redirect().back()
}
}
39 changes: 39 additions & 0 deletions app/controllers/admin/users/admin_store_users_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { HttpContext } from '@adonisjs/core/http'
import vine from '@vinejs/vine'
import User from '#models/user'
import Role from '../../../enums/role.js'

export default class AdminStoreUsersController {
static validator = vine.compile(
vine.object({
username: vine.string().minLength(3).maxLength(255),
email: vine.string().email(),
role: vine.enum(Role),
password: vine.string().minLength(3).maxLength(255),
passwordConfirmation: vine.string().confirmed({ confirmationField: 'password' }),
})
)

async store({ request, response, session }: HttpContext) {
const { username, email, role, password } = await request.validateUsing(
AdminStoreUsersController.validator
)

const existingUser = await User.query().where('email', email).first()

if (existingUser) {
session.flash('error', 'Email already in use !')
return response.redirect().back()
}

await User.create({
fullName: username,
role,
email,
password,
})

session.flash('success', 'User created successfully !')
return response.redirect().back()
}
}
44 changes: 44 additions & 0 deletions app/controllers/admin/users/admin_update_users_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import vine from '@vinejs/vine'
import Role from '#enums/role'
import { HttpContext } from '@adonisjs/core/http'
import User from '#models/user'

export default class AdminUpdateUsersController {
static validator = vine.compile(
vine.object({
username: vine.string().minLength(3).maxLength(255),
email: vine.string().email(),
role: vine.enum(Role),
})
)

async update({ request, response, session }: HttpContext) {
const { username, email, role } = await request.validateUsing(
AdminUpdateUsersController.validator
)

if (email === 'admin@knowledge.fr') {
session.flash('error', 'You can not update admin user')
return response.redirect().back()
}

const existingUser = await User.query()
.where('email', email)
.whereNot('id', request.param('id'))
.first()

if (existingUser) {
session.flash('error', 'Email already in use !')
return response.redirect().back()
}

const user = await User.findOrFail(request.param('id'))
user.fullName = username
user.email = email
user.role = role
await user.save()

session.flash('success', 'User created successfully !')
return response.redirect().back()
}
}
4 changes: 3 additions & 1 deletion app/controllers/profile/update_profile_controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HttpContext } from '@adonisjs/core/http'
import vine from '@vinejs/vine'
import UserDto from '../../dtos/users/user_dto.js'

export default class UpdateProfilesController {
static validator = vine.compile(
Expand All @@ -14,8 +15,9 @@ export default class UpdateProfilesController {

if (!user) throw new Error('User not found')

const dto = await UserDto.fromModel(user)
return inertia.render('profile/index', {
user,
user: dto,
})
}

Expand Down
25 changes: 25 additions & 0 deletions app/dtos/shares/share_admin_dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import env from '#start/env'
import Share from '#models/share'

export default class ShareListAdminDto {
declare id: number
declare url: string
declare created_at: string
declare folder_path: string

constructor(data: { id: number; url: string; folder_path: string; created_at: string }) {
this.id = data.id
this.url = data.url
this.folder_path = data.folder_path
this.created_at = data.created_at
}

static async fromModel(share: Share): Promise<ShareListAdminDto> {
return new ShareListAdminDto({
id: share.id,
url: `${env.get('APP_URL')}/shares/${share.token}/${share.folder.path}`,
folder_path: share.folder.path,
created_at: share.createdAt.toFormat('dd/MM/yyyy'),
})
}
}
25 changes: 25 additions & 0 deletions app/dtos/users/user_dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Role from '#enums/role'
import User from '#models/user'

export default class UserDto {
declare id: number
declare email: string
declare username: string
declare role: Role

constructor(data: { id: number; email: string; username: string; role: Role }) {
this.id = data.id
this.email = data.email
this.username = data.username
this.role = data.role
}

static async fromModel(user: User): Promise<UserDto> {
return new UserDto({
id: user.id,
email: user.email,
username: user.fullName,
role: user.role,
})
}
}
33 changes: 33 additions & 0 deletions app/dtos/users/users_listing_dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import User from '#models/user'

export default class UsersListingDto {
declare id: number
declare email: string
declare username: string
declare role: string
declare created_at: string

constructor(data: {
id: number
email: string
username: string
role: string
created_at: string
}) {
this.id = data.id
this.email = data.email
this.username = data.username
this.role = data.role
this.created_at = data.created_at
}

static async fromModel(user: User): Promise<UsersListingDto> {
return new UsersListingDto({
id: user.id,
email: user.email,
username: user.fullName,
role: user.roleLabel,
created_at: user.createdAt.toFormat('dd/MM/yyyy'),
})
}
}
6 changes: 6 additions & 0 deletions app/enums/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum Role {
ADMIN = 0,
MAINTAINER = 1,
}

export default Role
17 changes: 17 additions & 0 deletions app/middleware/role_middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
import Role from '../enums/role.js'

export default class RoleMiddleware {
async handle({ auth, response }: HttpContext, next: NextFn, options: { roles: Role[] }) {
const user = auth.user!

if (!options.roles.includes(user.role)) {
return response.forbidden({
message: 'You do not have permission to access this resource.',
})
}

return next()
}
}
10 changes: 9 additions & 1 deletion app/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import hash from '@adonisjs/core/services/hash'
import { compose } from '@adonisjs/core/helpers'
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { withAuthFinder } from '@adonisjs/auth/mixins/lucid'
import Role from '#enums/role'

const AuthFinder = withAuthFinder(() => hash.use('scrypt'), {
uids: ['email'],
Expand All @@ -14,17 +15,24 @@ export default class User extends compose(BaseModel, AuthFinder) {
declare id: number

@column()
declare fullName: string | null
declare fullName: string

@column()
declare email: string

@column({ serializeAs: null })
declare password: string

@column()
declare role: Role

@column.dateTime({ autoCreate: true })
declare createdAt: DateTime

@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime | null

get roleLabel(): string {
return Role[this.role]
}
}
6 changes: 5 additions & 1 deletion config/inertia.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineConfig } from '@adonisjs/inertia'
import type { InferSharedProps } from '@adonisjs/inertia/types'
import GetTreeByFolderAction from '../app/actions/get_tree_by_folder_action.js'
import UserDto from '../app/dtos/users/user_dto.js'

const inertiaConfig = defineConfig({
/**
Expand All @@ -12,7 +13,10 @@ const inertiaConfig = defineConfig({
* Data that should be shared with all rendered pages
*/
sharedData: {
user: (ctx) => ctx.inertia.always(() => ctx.auth.user),
user: (ctx) =>
ctx.inertia.always(async () =>
ctx.auth.user ? await UserDto.fromModel(ctx.auth.user) : null
),
flash: (ctx) => ({
success: ctx.session?.flashMessages.get('success'),
error: ctx.session?.flashMessages.get('error'),
Expand Down
15 changes: 15 additions & 0 deletions database/migrations/1773935989197_create_add_role_users_table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BaseSchema } from '@adonisjs/lucid/schema'

export default class extends BaseSchema {
async up() {
this.schema.table('users', (table) => {
table.integer('role').defaultTo(1)
})
}

async down() {
this.schema.table('users', (table) => {
table.dropColumn('role')
})
}
}
2 changes: 2 additions & 0 deletions database/seeders/user_seeder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BaseSeeder } from '@adonisjs/lucid/seeders'
import User from '#models/user'
import Role from '#enums/role'

export default class extends BaseSeeder {
async run() {
Expand All @@ -12,6 +13,7 @@ export default class extends BaseSeeder {
fullName: 'admin',
email: email,
password: 'knowledge',
role: Role.ADMIN,
})

console.log('Admin user created')
Expand Down
Loading
Loading