This commit is contained in:
Kiss Dávid
2026-03-21 12:34:22 +01:00
parent 465613f6a0
commit 9efbe5baee
42 changed files with 571 additions and 562 deletions
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+2 -1
View File
@@ -32,7 +32,8 @@ export default defineConfig({
() => import('@adonisjs/cors/cors_provider'),
() => import('@adonisjs/lucid/database_provider'),
() => import('@adonisjs/auth/auth_provider'),
() => import('@adonisjs/limiter/limiter_provider')
() => import('@adonisjs/limiter/limiter_provider'),
() => import('@adonisjs/ally/ally_provider'),
],
/*
+13 -21
View File
@@ -4,13 +4,12 @@ import User from '#models/user'
import logger from '@adonisjs/core/services/logger'
export default class AuthController {
/**
* Registers a user using the provided email, password, and full name.
*
* @bodyParam *email* - string | The email of the user.
* @bodyParam *password* - string | The password of the user.
* @bodyParam *fullName* - string | The full name of the user.
*
* @bodyParam *email* - string | The email of the user.
* @bodyParam *password* - string | The password of the user.
* @bodyParam *fullName* - string | The full name of the user.
* @returns The user access token.
*/
async register({ request }: HttpContext) {
@@ -19,16 +18,15 @@ export default class AuthController {
const user = await User.create(data)
logger.info('User registered: %s, %s, %s', user.fullName, user.email, request.ip())
return User.accessTokens.create(user)
}
catch(error) {
logger.error('Registration failed: %s',error)
throw(error)
} catch (error) {
logger.error('Registration failed: %s', error)
throw error
}
}
/**
* Log in a user using the provided email and password.
*
*
* @bodyParam *email* - string | The email of the user.
* @bodyParam *password* - string | The password of the user.
* @returns The access token of the user.
@@ -39,17 +37,12 @@ export default class AuthController {
const user = await User.verifyCredentials(email, password)
logger.info('User logged in: %s', email)
return User.accessTokens.create(user)
}
catch(error) {
logger.error('Login failed: %s',error)
} catch (error) {
logger.error('Login failed: %s', error)
throw error
}
}
/**
* Deletes the user's access token and logs them out. If the user is not
* authenticated, returns an unauthorized message.
@@ -60,10 +53,9 @@ export default class AuthController {
try {
await User.accessTokens.delete(user, user.currentAccessToken.identifier)
return { message: 'User logged out' }
}
catch(error){
logger.error('Logout failed: %s',error)
throw(error)
} catch (error) {
logger.error('Logout failed: %s', error)
throw error
}
}
+114 -102
View File
@@ -1,120 +1,132 @@
import type { HttpContext } from '@adonisjs/core/http'
import RestaurantMenuItem from '#models/restaurant_menu_item'
import RestaurantMenu from '#models/restaurant_menu'
import logger from '@adonisjs/core/services/logger'
import { menuItemValidator } from '#validators/menu'
export default class RestaurantMenuItemsController {
/**
* Retrieve all restaurant menu items.
*
* @returns An array of all restaurant menu items.
*/
async getAll() {
const items = await RestaurantMenuItem.query().preload('category').preload('menus')
return items
}
/**
* Retrieve all restaurant menu items.
*
* @returns An array of all restaurant menu items.
*/
async getAll() {
const items = await RestaurantMenuItem.all()
return items
/**
* Retrieve a single restaurant menu item.
*
* @param request - The HTTP context containing the request data.
* @returns The restaurant menu item with the specified id.
*/
async getOne({ request }: HttpContext) {
try {
const id = request.body().id
const item = await RestaurantMenuItem.query()
.where('id', id)
.preload('category')
.preload('menus')
.first()
return item
} catch (error) {
logger.error('Menu item retrieval failed: %s', error)
}
}
/**
* Retrieve a single restaurant menu item.
*
* @param request - The HTTP context containing the request data.
* @returns The restaurant menu item with the specified id.
*/
// The following methods require authentication. Log user activity.
async getOne({request}: HttpContext) {
try {
const id = request.body().id
const item = await RestaurantMenuItem.find(id)
return item
}
catch (error) {
logger.error('Menu item retrieval failed: %s',error)
}
/**
* Create a restaurant menu item.
*
* @bodyParam *name* - string | The name of the menu item.
* @bodyParam *priceSmall* - number | The price of the small serving of the menu item.
* @bodyParam *priceLarge* - number | The price of the large serving of the menu item.
* @bodyParam *description* - string | The description of the menu item.
* @bodyParam *menu_id* - number | The menu id of the menu item.
* @returns The created menu item.
*/
async create({ request, auth }: HttpContext) {
const data = await request.validateUsing(menuItemValidator)
try {
const item = await RestaurantMenuItem.create(data)
logger.info('Menu item created: %s by %s', item.name, auth.getUserOrFail().email)
return item
} catch (error) {
logger.error('Menu item creation failed: %s', error)
}
}
// The following methods require authentication. Log user activity.
/**
* Create a restaurant menu item.
*
* @bodyParam *name* - string | The name of the menu item.
* @bodyParam *priceSmall* - number | The price of the small serving of the menu item.
* @bodyParam *priceLarge* - number | The price of the large serving of the menu item.
* @bodyParam *description* - string | The description of the menu item.
* @bodyParam *menu_id* - number | The menu id of the menu item.
* @returns The created menu item.
*/
async create({request, auth}: HttpContext) {
const data = await request.validateUsing(menuItemValidator)
try {
const item = await RestaurantMenuItem.create(data)
logger.info('Menu item created: %s by %s', item.name, auth.getUserOrFail().email)
return item
}
catch(error) {
logger.error('Menu item creation failed: %s',error)
}
/**
* Update a restaurant menu item.
*
* @bodyParam *id* - number | The id of the menu item to update.
* @bodyParam *name* - string | The new name of the menu item.
* @bodyParam *priceSmall* - number | The new price of the small size of the menu item.
* @bodyParam *priceLarge* - number | The new price of the large size of the menu item.
* @bodyParam *description* - string | The new description of the menu item.
* @bodyParam *menu_id* - number | The new menu id of the menu item.
*/
async update({ request, auth }: HttpContext) {
const data = request.body()
try {
const item = await RestaurantMenuItem.find(data.id)
item?.merge(data)
await item?.save()
logger.info('Menu item updated: %s by %s', item?.name, auth.getUserOrFail().email)
} catch (error) {
logger.error('Menu item update failed: %s', error)
}
}
/**
* Update a restaurant menu item.
*
* @bodyParam *id* - number | The id of the menu item to update.
* @bodyParam *name* - string | The new name of the menu item.
* @bodyParam *priceSmall* - number | The new price of the small size of the menu item.
* @bodyParam *priceLarge* - number | The new price of the large size of the menu item.
* @bodyParam *description* - string | The new description of the menu item.
* @bodyParam *menu_id* - number | The new menu id of the menu item.
*/
async update({request, auth}: HttpContext) {
const data = request.body()
try {
const item = await RestaurantMenuItem.find(data.id)
item?.merge(data)
await item?.save()
logger.info('Menu item updated: %s by %s', item?.name, auth.getUserOrFail().email)
}
catch(error) {
logger.error('Menu item update failed: %s',error)
}
/**
* Delete a restaurant menu item.
*
* @bodyParam *id* - number | The id of the menu item to delete.
*/
async delete({ request, auth, response }: HttpContext) {
const id = request.body().id
try {
const item = await RestaurantMenuItem.find(id)
if (!item) {
return response.notFound({ message: 'Menu item not found' })
}
// Detach from all menus first
await item.related('menus').detach()
await item.delete()
logger.info('Menu item deleted: %s by %s', item.name, auth.getUserOrFail().email)
return response.ok({ message: 'Menu item deleted' })
} catch (error) {
logger.error('Menu item deletion failed: %s', error)
return response.internalServerError({ message: 'Menu item deletion failed' })
}
}
/**
* Delete a restaurant menu item.
*
* @bodyParam *id* - number | The id of the menu item to delete.
*/
async delete({request, auth}: HttpContext) {
const id = request.body().id
try {
const item = await RestaurantMenuItem.find(id)
await item?.delete()
logger.info('Menu item deleted: %s by %s', item?.name, auth.getUserOrFail().email)
}
catch(error) {
logger.error('Menu item deletion failed: %s',error)
}
}
// This method connects the menu item to a menu.
// This method connects the menu item to a menu.
/**
* Connect a restaurant menu item to a restaurant menu.
*
* @bodyParam *item_id* - number | The id of the menu item to connect.
* @bodyParam *menu_id* - number | The id of the menu to connect the menu item to.
*/
async connectToMenu({request}: HttpContext) {
const data = request.body()
try {
const item = await RestaurantMenuItem.find(data.item_id)
const menu = await data.menu_id
item?.merge({menu_id: menu})
await item?.save()
}
catch(error) {
logger.error('Menu item connection failed: %s',error)
}
/**
* Connect a restaurant menu item to a restaurant menu.
*
* @bodyParam *item_id* - number | The id of the menu item to connect.
* @bodyParam *menu_id* - number | The id of the menu to connect the menu item to.
*/
async connectToMenu({ request, response }: HttpContext) {
const data = request.body()
try {
const item = await RestaurantMenuItem.find(data.item_id)
const menu = await RestaurantMenu.find(data.menu_id)
if (!item || !menu) {
return response.notFound({ message: 'Menu or Item not found' })
}
await item.related('menus').attach([menu.id])
return response.ok({ message: 'Menu item connected to menu' })
} catch (error) {
logger.error('Menu item connection failed: %s', error)
return response.internalServerError({ message: 'Menu item connection failed' })
}
}
}
}
+87 -78
View File
@@ -1,90 +1,99 @@
// import type { HttpContext } from '@adonisjs/core/http'
import RestaurantMenu from "#models/restaurant_menu"
import { HttpContext } from "@adonisjs/core/http"
import logger from "@adonisjs/core/services/logger"
import RestaurantMenu from '#models/restaurant_menu'
import { HttpContext } from '@adonisjs/core/http'
import logger from '@adonisjs/core/services/logger'
export default class RestaurantMenusController {
/**
* Retrieve all restaurant menus.
*
* @returns An array of all restaurant menus.
*/
async getAll() {
const menus = await RestaurantMenu.all()
return menus
}
/**
* Retrieve a restaurant menu.
*
* @bodyParam id number The id of the menu to retrieve.
* @returns The retrieved menu.
*/
async getOne({ request }: HttpContext) {
const id = request.body().id
try {
const menu = await RestaurantMenu.find(id)
return menu
}
catch (error) {
logger.error('Menu retrieval failed: %s', error)
}
/**
* Retrieve all restaurant menus.
*
* @returns An array of all restaurant menus.
*/
async getAll() {
const menus = await RestaurantMenu.query().preload('items', (itemsQuery) => {
itemsQuery.preload('category')
})
return menus
}
/**
* Retrieve a restaurant menu.
*
* @bodyParam id number The id of the menu to retrieve.
* @returns The retrieved menu.
*/
async getOne({ request }: HttpContext) {
const id = request.body().id
try {
const menu = await RestaurantMenu.query()
.where('id', id)
.preload('items', (itemsQuery) => {
itemsQuery.preload('category')
})
.first()
return menu
} catch (error) {
logger.error('Menu retrieval failed: %s', error)
}
}
/**
* Create a restaurant menu.
*
* @bodyParam name string The name of the menu.
* @bodyParam description string The description of the menu.
* @returns The created menu.
*/
async create({ request, auth }: HttpContext) {
const data = request.body()
try {
const menu = await RestaurantMenu.create(data)
logger.info('Menu created: %s by %s', menu.name, auth.getUserOrFail().email)
return menu
}
catch (error) {
logger.error('Menu creation failed: %s', error)
}
/**
* Create a restaurant menu.
*
* @bodyParam name string The name of the menu.
* @bodyParam description string The description of the menu.
* @returns The created menu.
*/
async create({ request, auth }: HttpContext) {
const data = request.body()
try {
const menu = await RestaurantMenu.create(data)
logger.info('Menu created: %s by %s', menu.name, auth.getUserOrFail().email)
return menu
} catch (error) {
logger.error('Menu creation failed: %s', error)
}
}
/**
* Update a restaurant menu.
*
* @bodyParam id number The id of the menu to update.
* @bodyParam name string The new name of the menu.
*/
async update({ request, auth }: HttpContext) {
const data = request.body()
try {
const menu = await RestaurantMenu.find(data.id)
menu?.merge(data)
await menu?.save()
logger.info('Menu updated: %s by %s', menu?.name, auth.getUserOrFail().email)
}
catch (error) {
logger.error('Menu update failed: %s', error)
}
/**
* Update a restaurant menu.
*
* @bodyParam id number. The id of the menu to update.
* @bodyParam name string. The new name of the menu.
*/
async update({ request, auth }: HttpContext) {
const data = request.body()
try {
const menu = await RestaurantMenu.find(data.id)
menu?.merge(data)
await menu?.save()
logger.info('Menu updated: %s by %s', menu?.name, auth.getUserOrFail().email)
} catch (error) {
logger.error('Menu update failed: %s', error)
}
}
/**
* Delete a restaurant menu.
*
* @bodyParam id number. The id of the menu to delete.
*/
async delete({ request, auth, response }: HttpContext) {
const id = request.body().id
try {
const menu = await RestaurantMenu.find(id)
if (!menu) {
return response.notFound({ message: 'Menu not found' })
}
/**
* Delete a restaurant menu.
*
* @bodyParam id number The id of the menu to delete.
*/
async delete({ request, auth }: HttpContext) {
const id = request.body().id
try {
const menu = await RestaurantMenu.find(id)
await menu?.delete()
logger.info('Menu deleted: %s by %s', menu?.name, auth.getUserOrFail().email)
}
catch (error) {
logger.error('Menu deletion failed: %s', error)
}
// Detach all menu items before deleting the menu
await menu.related('items').detach()
await menu.delete()
logger.info('Menu deleted: %s by %s', menu.name, auth.getUserOrFail().email)
return response.ok({ message: 'Menu deleted' })
} catch (error) {
logger.error('Menu deletion failed: %s', error)
return response.internalServerError({ message: 'Menu deletion failed' })
}
}
}
}
Regular → Executable
View File
View File
View File
View File
Regular → Executable
+13 -5
View File
@@ -1,6 +1,6 @@
import { DateTime } from 'luxon'
import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
import type { HasMany } from '@adonisjs/lucid/types/relations'
import { BaseModel, column, manyToMany } from '@adonisjs/lucid/orm'
import type { ManyToMany } from '@adonisjs/lucid/types/relations'
import RestaurantMenuItem from './restaurant_menu_item.js'
export default class RestaurantMenu extends BaseModel {
@@ -16,6 +16,14 @@ export default class RestaurantMenu extends BaseModel {
@column()
declare name: string
@hasMany(() => RestaurantMenuItem)
declare items: HasMany<typeof RestaurantMenuItem>
}
@column({ columnName: 'name_hu' })
declare nameHu: string | null
@column({ columnName: 'name_en' })
declare nameEn: string | null
@manyToMany(() => RestaurantMenuItem, {
pivotTable: 'menu_item_menus',
})
declare items: ManyToMany<typeof RestaurantMenuItem>
}
+25 -5
View File
@@ -1,5 +1,8 @@
import { DateTime } from 'luxon'
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { BaseModel, column, belongsTo, manyToMany } from '@adonisjs/lucid/orm'
import type { BelongsTo, ManyToMany } from '@adonisjs/lucid/types/relations'
import Category from '#models/category'
import RestaurantMenu from '#models/restaurant_menu'
export default class RestaurantMenuItem extends BaseModel {
@column({ isPrimary: true })
@@ -14,6 +17,12 @@ export default class RestaurantMenuItem extends BaseModel {
@column()
declare name: string
@column({ columnName: 'name_hu' })
declare nameHu: string | null
@column({ columnName: 'name_en' })
declare nameEn: string | null
@column()
declare priceSmall: number
@@ -23,9 +32,20 @@ export default class RestaurantMenuItem extends BaseModel {
@column()
declare description: string | null
//connet to restaurant_menu
@column()
declare menu_id: number | null
@column({ columnName: 'description_hu' })
declare descriptionHu: string | null
@column({ columnName: 'description_en' })
declare descriptionEn: string | null
}
@column({ columnName: 'category_id' })
declare categoryId: number | null
@belongsTo(() => Category)
declare category: BelongsTo<typeof Category>
@manyToMany(() => RestaurantMenu, {
pivotTable: 'menu_item_menus',
})
declare menus: ManyToMany<typeof RestaurantMenu>
}
Regular → Executable
+3
View File
@@ -20,6 +20,9 @@ export default class User extends compose(BaseModel, AuthFinder) {
@column()
declare email: string
@column()
declare role: string
@column({ serializeAs: null })
declare password: string
Regular → Executable
+2 -1
View File
@@ -13,7 +13,8 @@ export const registerValidator = vine.compile(
return !match
}),
password,
fullName: vine.string()
fullName: vine.string(),
role: vine.string().optional(),
})
)
export const loginValidator = vine.compile(
Regular → Executable
+12 -8
View File
@@ -1,10 +1,14 @@
import vine from "@vinejs/vine";
import vine from '@vinejs/vine'
export const menuItemValidator = vine.compile(
vine.object({
name: vine.string(),
priceSmall: vine.number(),
priceLarge: vine.number(),
description: vine.string(),
})
);
vine.object({
name: vine.string(),
nameHu: vine.string(),
nameEn: vine.string(),
priceSmall: vine.number(),
priceLarge: vine.number(),
description: vine.string().optional(),
descriptionHu: vine.string().optional(),
descriptionEn: vine.string().optional(),
})
)
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1 -6
View File
@@ -1,5 +1,4 @@
services:
db:
image: postgres
ports:
@@ -17,8 +16,4 @@ services:
POSTGRES_PASSWORD: Paprika_2025
POSTGRES_USER: paprika
POSTGRES_DB: paprika
adminer:
image: adminer
restart: always
ports:
- 8080:8080
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -21,4 +21,4 @@ const dbConfig = defineConfig({
},
})
export default dbConfig
export default dbConfig
Regular → Executable
View File
Regular → Executable
+8 -10
View File
@@ -7,19 +7,18 @@ const limiterConfig = defineConfig({
/**
* Database store to save rate limiting data inside a
* MYSQL or PostgreSQL database.
*/
database: stores.database({
connectionName: 'postgres',
dbName: env.get('DB_DATABASE'),
tableName: 'rate_limits',
clearExpiredByTimeout: false
*/
database: stores.database({
connectionName: 'postgres',
dbName: env.get('DB_DATABASE'),
tableName: 'rate_limits',
clearExpiredByTimeout: false,
}),
/**
* Memory store could be used during
* testing
*/
memory: stores.memory({}),
*/
memory: stores.memory({}),
},
})
@@ -28,4 +27,3 @@ export default limiterConfig
declare module '@adonisjs/limiter/types' {
export interface LimitersList extends InferLimiters<typeof limiterConfig> {}
}
Regular → Executable
+1 -1
View File
@@ -17,7 +17,7 @@ const loggerConfig = defineConfig({
transport: {
targets: targets()
.pushIf(!app.inProduction, targets.pretty())
.pushIf(app.inProduction, targets.file({ destination: 1 }))
.pushIf(app.inProduction, targets.file({ destination: './logs/app.log' }))
.toArray(),
},
},
@@ -1,21 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'users'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').notNullable()
table.string('full_name').nullable()
table.string('email', 254).notNullable().unique()
table.string('password').notNullable()
table.timestamp('created_at').notNullable()
table.timestamp('updated_at').nullable()
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
@@ -1,31 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'auth_access_tokens'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table
.integer('tokenable_id')
.notNullable()
.unsigned()
.references('id')
.inTable('users')
.onDelete('CASCADE')
table.string('type').notNullable()
table.string('name').nullable()
table.string('hash').notNullable()
table.text('abilities').notNullable()
table.timestamp('created_at')
table.timestamp('updated_at')
table.timestamp('last_used_at').nullable()
table.timestamp('expires_at').nullable()
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
@@ -1,18 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'dbs'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table.timestamp('created_at')
table.timestamp('updated_at')
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
@@ -1,17 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'rate_limits'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.string('key', 255).notNullable().primary()
table.integer('points', 9).notNullable().defaultTo(0)
table.bigint('expire').unsigned()
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
@@ -1,20 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'restaurant_menus'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.string('name').notNullable()
table.timestamp('created_at')
table.timestamp('updated_at')
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
@@ -1,23 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'restaurant_menu_items'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.integer('restaurant_menu_id').unsigned().references('restaurant_menus.id')
table.string('name').notNullable()
table.integer('price_small').notNullable()
table.integer('price_large').notNullable()
table.string('description').nullable()
table.timestamp('created_at')
table.timestamp('updated_at')
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
Regular → Executable
View File
Generated Regular → Executable
+162 -156
View File
@@ -9,6 +9,7 @@
"version": "0.0.0",
"license": "UNLICENSED",
"dependencies": {
"@adonisjs/ally": "^5.0.0",
"@adonisjs/auth": "^9.3.1",
"@adonisjs/core": "^6.17.2",
"@adonisjs/cors": "^2.2.1",
@@ -61,6 +62,22 @@
"node": ">=18.16.0"
}
},
"node_modules/@adonisjs/ally": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@adonisjs/ally/-/ally-5.1.1.tgz",
"integrity": "sha512-Uu+g5y7jnmMP0iXhPYTx9caJnovp+CTqxDN6XpyuLQGVZjNr9fqbPeaBVs1/jTAVSAtpv4UrHsb+827XXd24Jw==",
"license": "MIT",
"dependencies": {
"@poppinss/oauth-client": "^7.0.1",
"@poppinss/utils": "^6.10.1"
},
"engines": {
"node": ">=20.6.0"
},
"peerDependencies": {
"@adonisjs/core": "^6.2.0"
}
},
"node_modules/@adonisjs/application": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/@adonisjs/application/-/application-8.3.1.tgz",
@@ -152,22 +169,22 @@
}
},
"node_modules/@adonisjs/bodyparser": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/@adonisjs/bodyparser/-/bodyparser-10.0.3.tgz",
"integrity": "sha512-rgnAE+w7/+8tlCG+GxT8/99TLyccMFHtUyIP99K4YbSg0af6kTHrbXOJkKE8YHKU8cFwECybu3uZOoIoHOYLyQ==",
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@adonisjs/bodyparser/-/bodyparser-10.1.2.tgz",
"integrity": "sha512-Lp419XmpMOq878Ee9V1Iqu2vGHEFBMUrUK3A3Fz3/dCP70E40esRf7wzTvRe+YZfXqQLaz1XCE0xfy3lj9wA9Q==",
"license": "MIT",
"dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"@poppinss/macroable": "^1.0.4",
"@poppinss/macroable": "^1.1.0",
"@poppinss/multiparty": "^2.0.1",
"@poppinss/utils": "^6.9.2",
"@types/qs": "^6.9.18",
"@poppinss/utils": "^6.10.1",
"@types/qs": "^6.14.0",
"bytes": "^3.1.2",
"file-type": "^20.1.0",
"file-type": "^21.1.1",
"inflation": "^2.1.0",
"media-typer": "^1.1.0",
"qs": "^6.14.0",
"raw-body": "^3.0.0"
"raw-body": "^3.0.1"
},
"engines": {
"node": ">=18.16.0"
@@ -634,6 +651,16 @@
"node": ">=6.9.0"
}
},
"node_modules/@borewit/text-codec": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz",
"integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/@chevrotain/cst-dts-gen": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
@@ -1007,7 +1034,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@japa/assert/-/assert-4.0.1.tgz",
"integrity": "sha512-n/dA9DVLNvM/Bw8DtN8kBdPjYsSHe3XTRjF5+U8vlzDavpW9skUANl2CHR1K/TBWZxwMfGi15SJIjo6UCs09AA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@poppinss/macroable": "^1.0.4",
@@ -1165,15 +1192,6 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@lukeed/ms": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
"integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@noble/hashes": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
@@ -1320,13 +1338,10 @@
}
},
"node_modules/@poppinss/exception": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.1.tgz",
"integrity": "sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A==",
"license": "MIT",
"engines": {
"node": ">=18"
}
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
"integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
"license": "MIT"
},
"node_modules/@poppinss/hooks": {
"version": "7.2.5",
@@ -1338,13 +1353,10 @@
}
},
"node_modules/@poppinss/macroable": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@poppinss/macroable/-/macroable-1.0.4.tgz",
"integrity": "sha512-ct43jurbe7lsUX5eIrj4ijO3j/6zIPp7CDnFWXDs7UPAbw1Pu1iH3oAmFdP4jcskKJBURH5M9oTtyeiUXyHX8Q==",
"license": "MIT",
"engines": {
"node": ">=18.16.0"
}
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@poppinss/macroable/-/macroable-1.1.0.tgz",
"integrity": "sha512-y/YKzZDuG8XrpXpM7Z1RdQpiIc0MAKyva24Ux1PB4aI7RiSI/79K8JVDcdyubriTm7vJ1LhFs8CrZpmPnx/8Pw==",
"license": "MIT"
},
"node_modules/@poppinss/matchit": {
"version": "3.1.2",
@@ -1375,6 +1387,19 @@
"uid-safe": "2.1.5"
}
},
"node_modules/@poppinss/oauth-client": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@poppinss/oauth-client/-/oauth-client-7.0.2.tgz",
"integrity": "sha512-7zFnfGtd7oLb9Ox9Kz77QwU8t4IV8PdHIicSkLgXGis1ZfLb/853dm+26xz0dPo0qZCJvbBVelgmDvm+JFMhQw==",
"license": "MIT",
"dependencies": {
"@poppinss/exception": "^1.2.2",
"ky": "^1.14.1"
},
"engines": {
"node": ">=24.0.0"
}
},
"node_modules/@poppinss/object-builder": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@poppinss/object-builder/-/object-builder-1.1.0.tgz",
@@ -1400,36 +1425,29 @@
}
},
"node_modules/@poppinss/string": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@poppinss/string/-/string-1.2.0.tgz",
"integrity": "sha512-1z78zjqhfjqsvWr+pQzCpRNcZpIM+5vNY5SFOvz28GrL/LRanwtmOku5tBX7jE8/ng3oXaOVrB59lnnXFtvkug==",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@poppinss/string/-/string-1.7.1.tgz",
"integrity": "sha512-OrLzv/nGDU6l6dLXIQHe8nbNSWWfuSbpB/TW5nRpZFf49CLuQlIHlSPN9IdSUv2vG+59yGM6LoibsaHn8B8mDw==",
"license": "MIT",
"dependencies": {
"@lukeed/ms": "^2.0.2",
"@types/bytes": "^3.1.5",
"@types/pluralize": "^0.0.33",
"bytes": "^3.1.2",
"case-anything": "^3.1.0",
"case-anything": "^3.1.2",
"pluralize": "^8.0.0",
"slugify": "^1.6.6",
"truncatise": "^0.0.8"
},
"engines": {
"node": ">=20.6.0"
"slugify": "^1.6.6"
}
},
"node_modules/@poppinss/utils": {
"version": "6.9.2",
"resolved": "https://registry.npmjs.org/@poppinss/utils/-/utils-6.9.2.tgz",
"integrity": "sha512-ypVszZxhwiehhklM5so2BI+nClQJwp7mBUSJh/R1GepeUH1vvD5GtxMz8Lp9dO9oAbKyDmq1jc4g/4E0dv8r2g==",
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/@poppinss/utils/-/utils-6.10.1.tgz",
"integrity": "sha512-da+MMyeXhBaKtxQiWPfy7+056wk3lVIhioJnXHXkJ2/OHDaZfFcyKHNl1R06sdYO8lIRXcXdoZ6LO2ARmkAREA==",
"license": "MIT",
"dependencies": {
"@poppinss/exception": "^1.2.0",
"@poppinss/exception": "^1.2.1",
"@poppinss/object-builder": "^1.1.0",
"@poppinss/string": "^1.1.0",
"@poppinss/string": "^1.3.0",
"flattie": "^1.1.1",
"safe-stable-stringify": "^2.5.0",
"secure-json-parse": "^3.0.1"
"secure-json-parse": "^4.0.0"
},
"engines": {
"node": ">=18.16.0"
@@ -1732,14 +1750,13 @@
}
},
"node_modules/@tokenizer/inflate": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
"integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
"integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"fflate": "^0.8.2",
"token-types": "^6.0.0"
"debug": "^4.4.3",
"token-types": "^6.1.1"
},
"engines": {
"node": ">=18"
@@ -1796,17 +1813,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/bytes": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.5.tgz",
"integrity": "sha512-VgZkrJckypj85YxEsEavcMmmSOIzkUHqWmM4CCyia5dc54YwsXzJ5uT4fYxBQNEXx+oF1krlhgCbvfubXqZYsQ==",
"license": "MIT"
},
"node_modules/@types/chai": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.1.tgz",
"integrity": "sha512-iu1JLYmGmITRzUgNiLMZD3WCoFzpYtueuyAgHTXqgwSRAMIlFTnZqG6/xenkpUGRJEzSfklUTI4GNSzks/dc0w==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*"
@@ -1823,7 +1834,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/estree": {
@@ -1884,9 +1895,9 @@
"license": "MIT"
},
"node_modules/@types/qs": {
"version": "6.9.18",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
"license": "MIT"
},
"node_modules/@types/superagent": {
@@ -2271,7 +2282,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -2342,9 +2353,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -2507,7 +2518,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
"integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"assertion-error": "^2.0.1",
@@ -2563,7 +2574,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 16"
@@ -2955,9 +2966,9 @@
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -3000,7 +3011,7 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -3697,12 +3708,6 @@
"reusify": "^1.0.4"
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"license": "MIT"
},
"node_modules/figures": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
@@ -3733,18 +3738,18 @@
}
},
"node_modules/file-type": {
"version": "20.4.1",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz",
"integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==",
"version": "21.3.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz",
"integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==",
"license": "MIT",
"dependencies": {
"@tokenizer/inflate": "^0.2.6",
"strtok3": "^10.2.0",
"token-types": "^6.0.0",
"@tokenizer/inflate": "^0.4.1",
"strtok3": "^10.3.4",
"token-types": "^6.1.1",
"uint8array-extras": "^1.4.0"
},
"engines": {
"node": ">=18"
"node": ">=20"
},
"funding": {
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
@@ -4195,19 +4200,23 @@
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/human-signals": {
@@ -4221,15 +4230,19 @@
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz",
"integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/ieee754": {
@@ -4525,9 +4538,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4715,6 +4728,18 @@
"node": ">=8"
}
},
"node_modules/ky": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/ky/-/ky-1.14.1.tgz",
"integrity": "sha512-hYje4L9JCmpEQBtudo+v52X5X8tgWXUYyPcxKSuxQNboqufecl9VMWjGiucAFH060AwPXHZuH+WB2rrqfkmafw==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sindresorhus/ky?sponsor=1"
}
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -4865,7 +4890,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
"integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/lru-cache": {
@@ -5457,25 +5482,12 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
"integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 14.16"
}
},
"node_modules/peek-readable": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz",
"integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/pg": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz",
@@ -5974,9 +5986,9 @@
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -6047,18 +6059,18 @@
"license": "ISC"
},
"node_modules/raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
"node": ">= 0.10"
}
},
"node_modules/react-is": {
@@ -6478,9 +6490,9 @@
"license": "MIT"
},
"node_modules/secure-json-parse": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz",
"integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
"integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
"funding": [
{
"type": "github",
@@ -6795,9 +6807,9 @@
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -6899,13 +6911,12 @@
}
},
"node_modules/strtok3": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz",
"integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==",
"version": "10.3.4",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz",
"integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==",
"license": "MIT",
"dependencies": {
"@tokenizer/token": "^0.3.0",
"peek-readable": "^7.0.0"
"@tokenizer/token": "^0.3.0"
},
"engines": {
"node": ">=18"
@@ -7079,11 +7090,12 @@
}
},
"node_modules/token-types": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz",
"integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==",
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz",
"integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==",
"license": "MIT",
"dependencies": {
"@borewit/text-codec": "^0.2.1",
"@tokenizer/token": "^0.3.0",
"ieee754": "^1.2.1"
},
@@ -7095,12 +7107,6 @@
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/truncatise": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/truncatise/-/truncatise-0.0.8.tgz",
"integrity": "sha512-cXzueh9pzBCsLzhToB4X4gZCb3KYkrsAcBAX97JnazE74HOl3cpBJYEV7nabHeG/6/WXCU5Yujlde/WPBUwnsg==",
"license": "MIT"
},
"node_modules/ts-api-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
@@ -7287,9 +7293,9 @@
}
},
"node_modules/uint8array-extras": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz",
"integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
"integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -7387,9 +7393,9 @@
}
},
"node_modules/validator": {
"version": "13.15.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz",
"integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==",
"version": "13.15.26",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz",
"integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
Regular → Executable
+2 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"license": "UNLICENSED",
"scripts": {
"start": "node bin/server.js",
"start": "node bin/server.ts",
"build": "node ace build",
"dev": "node ace serve --hmr",
"test": "node ace test",
@@ -52,6 +52,7 @@
},
"dependencies": {
"@adonisjs/auth": "^9.3.1",
"@adonisjs/ally": "^5.0.0",
"@adonisjs/core": "^6.17.2",
"@adonisjs/cors": "^2.2.1",
"@adonisjs/limiter": "^2.4.0",
Regular → Executable
View File
Regular → Executable
+6 -2
View File
@@ -32,12 +32,16 @@ server.use([
* The router middleware stack runs middleware on all the HTTP
* requests with a registered route.
*/
router.use([() => import('@adonisjs/core/bodyparser_middleware'), () => import('@adonisjs/auth/initialize_auth_middleware')])
router.use([
() => import('@adonisjs/core/bodyparser_middleware'),
() => import('@adonisjs/auth/initialize_auth_middleware'),
])
/**
* Named middleware collection must be explicitly assigned to
* the routes or the routes group.
*/
export const middleware = router.named({
auth: () => import('#middleware/auth_middleware')
auth: () => import('#middleware/auth_middleware'),
admin: () => import('#middleware/admin_middleware'),
})
Regular → Executable
+115 -18
View File
@@ -14,26 +14,123 @@ import { throttle } from '#start/limiter'
const AuthController = () => import('#controllers/auth_controller')
const RestaurantMenuItemsController = () => import('#controllers/restaurant_menu_items_controller')
const RestaurantMenusController = () => import('#controllers/restaurant_menus_controller')
const CategoriesController = () => import('#controllers/categories_controller')
const UsersController = () => import('#controllers/users_controller')
const ImagesController = () => import('#controllers/images_controller')
//Auth routes
router.post('/register', [AuthController, 'register']).as('auth.register').use(throttle)
router.post('/login', [AuthController, 'login']).as('auth.login').use(throttle)
router.delete('/logout', [AuthController, 'logout']).as('auth.logout').use(middleware.auth())
router.get('/me', [AuthController, 'me']).as('auth.me').use(middleware.auth())
// Auth routes
router.group(() => {
router.post('/register', [AuthController, 'register']).as('auth.register').use(throttle)
router.post('/login', [AuthController, 'login']).as('auth.login').use(throttle)
router.delete('/logout', [AuthController, 'logout']).as('auth.logout').use(middleware.auth())
router.get('/me', [AuthController, 'me']).as('auth.me').use(middleware.auth()).use(throttle)
})
// RestaurantMenuItem routes
router.get('/api/menu-items', [RestaurantMenuItemsController, 'getAll']).as('menu-items.getAll')
router.post('/api/menu-items', [RestaurantMenuItemsController, 'getOne']).as('menu-items.getOne')
router.post('/api/menu-items/create', [RestaurantMenuItemsController, 'create']).as('menu-items.create').use(middleware.auth())
router.put('/api/menu-items', [RestaurantMenuItemsController, 'update']).as('menu-items.update').use(middleware.auth())
router.delete('/api/menu-items', [RestaurantMenuItemsController, 'delete']).as('menu-items.delete').use(middleware.auth()).use(throttle)
router
.group(() => {
router
.group(() => {
router.get('/', [RestaurantMenuItemsController, 'getAll']).as('menu-items.getAll')
router
.post('/', [RestaurantMenuItemsController, 'getOne'])
.as('menu-items.getOne')
.use([middleware.auth(), middleware.admin()])
router
.post('/create', [RestaurantMenuItemsController, 'create'])
.as('menu-items.create')
.use([middleware.auth(), middleware.admin()])
router
.put('/', [RestaurantMenuItemsController, 'update'])
.as('menu-items.update')
.use([middleware.auth(), middleware.admin()])
router
.delete('/', [RestaurantMenuItemsController, 'delete'])
.as('menu-items.delete')
.use([middleware.auth(), middleware.admin(), throttle])
router
.post('/attach', [RestaurantMenuItemsController, 'connectToMenu'])
.as('menu-items.attach')
.use([middleware.auth(), middleware.admin()])
})
.prefix('/menu-items')
// RestaurantMenu routes
router
.group(() => {
router.get('/', [RestaurantMenusController, 'getAll']).as('menus.getAll')
router
.post('/', [RestaurantMenusController, 'getOne'])
.as('menus.getOne')
.use([middleware.auth(), middleware.admin()])
router
.post('/create', [RestaurantMenusController, 'create'])
.as('menus.create')
.use([middleware.auth(), middleware.admin()])
router
.put('/', [RestaurantMenusController, 'update'])
.as('menus.update')
.use([middleware.auth(), middleware.admin()])
router
.delete('/', [RestaurantMenusController, 'delete'])
.as('menus.delete')
.use([middleware.auth(), middleware.admin(), throttle])
})
.prefix('/menus')
// Category routes
router
.group(() => {
router.get('/', [CategoriesController, 'getAll']).as('categories.getAll')
router
.post('/', [CategoriesController, 'getOne'])
.as('categories.getOne')
.use([middleware.auth(), middleware.admin()])
router
.post('/create', [CategoriesController, 'create'])
.as('categories.create')
.use([middleware.auth(), middleware.admin()])
router
.put('/', [CategoriesController, 'update'])
.as('categories.update')
.use([middleware.auth(), middleware.admin()])
router
.delete('/', [CategoriesController, 'delete'])
.as('categories.delete')
.use([middleware.auth(), middleware.admin(), throttle])
})
.prefix('/categories')
// RestaurantMenu routes
router.get('/api/menus', [RestaurantMenusController, 'getAll']).as('menus.getAll')
router.post('/api/menus', [RestaurantMenusController, 'getOne']).as('menus.getOne')
router.post('/api/menus/create', [RestaurantMenusController, 'create']).as('menus.create').use(middleware.auth())
router.put('/api/menus', [RestaurantMenusController, 'update']).as('menus.update').use(middleware.auth())
router.delete('/api/menus', [RestaurantMenusController, 'delete']).as('menus.delete').use(middleware.auth()).use(throttle)
// User routes
router
.group(() => {
router.get('/', [UsersController, 'getAll']).as('users.getAll').use(middleware.auth())
router
.post('/', [UsersController, 'create'])
.as('users.create')
.use([middleware.auth(), middleware.admin()])
router
.post('/get-one', [UsersController, 'getOne'])
.as('users.getOne')
.use([middleware.auth(), middleware.admin()])
router
.put('/', [UsersController, 'update'])
.as('users.update')
.use([middleware.auth(), middleware.admin()])
router
.delete('/', [UsersController, 'delete'])
.as('users.delete')
.use([middleware.auth(), middleware.admin()])
})
.prefix('/users')
// Image routes
router
.group(() => {
router
.post('/upload', [ImagesController, 'upload'])
.as('images.upload')
.use([middleware.auth(), middleware.admin()])
router.get('/:filename', [ImagesController, 'show']).as('images.show')
})
.prefix('/images')
})
.prefix('/api')
Regular → Executable
+2 -2
View File
@@ -5,7 +5,6 @@ import type { Config } from '@japa/runner/types'
import { pluginAdonisJS } from '@japa/plugin-adonisjs'
import testUtils from '@adonisjs/core/services/test_utils'
/**
* This file is imported by the "bin/test.ts" entrypoint file
*/
@@ -19,7 +18,8 @@ export const plugins: Config['plugins'] = [
apiClient({
baseURL: 'http://localhost:3333',
}),
pluginAdonisJS(app)]
pluginAdonisJS(app),
]
/**
* Configure lifecycle function to run before and after all the
+2 -14
View File
@@ -1,21 +1,9 @@
import { test } from '@japa/runner'
test.group('Restaurant menu items', () => {
test('create', async ({ assert, client }) => {
const item = {
name: 'test',
priceSmall: 10,
priceLarge: 20,
description: 'test'
}
const response = await client.post('/api/menu-items/create').json(item)
assert.equal(response.body().name, item.name)
assert.equal(response.body().priceSmall, item.priceSmall)
assert.equal(response.body().priceLarge, item.priceLarge)
assert.equal(response.body().description, item.description)
}),
test('get all', async ({ assert, client }) => {
const response = await client.get('/api/menu-items')
assert.equal(response.status(), 200)
assert.isArray(response.body())
})
})
})
Regular → Executable
View File