130 lines
4.2 KiB
TypeScript
130 lines
4.2 KiB
TypeScript
import Meal from '#models/meal'
|
|
import MealRegistration from '#models/meal_registration'
|
|
import { credentialsValidator } from '#validators/auth'
|
|
import { createMealValidator } from '#validators/meal'
|
|
import type { HttpContext } from '@adonisjs/core/http'
|
|
import redis from '@adonisjs/redis/services/main'
|
|
import { DateTime } from 'luxon'
|
|
|
|
export default class MealsController {
|
|
// POST /meals
|
|
public async create({ request }: HttpContext) {
|
|
const data = await request.validateUsing(createMealValidator)
|
|
|
|
if ('meals' in data) {
|
|
// Remove all already submittable meals
|
|
await Meal.query().where('submittable', true).update({ submittable: false })
|
|
// Set new meals as submittable
|
|
for (const mealData of data.meals) {
|
|
const meal = await Meal.query()
|
|
.where('date', mealData.date)
|
|
.where('type', mealData.meal_type === 'lunch' ? 0 : 1)
|
|
.first()
|
|
if (meal) {
|
|
meal.submittable = true
|
|
await meal.save()
|
|
} else {
|
|
console.warn('Meal to set as submittable not found:', mealData)
|
|
}
|
|
}
|
|
return { message: 'Meals updated successfully' }
|
|
}
|
|
|
|
const { date, type, courses } = data
|
|
const mealType = type === 'Déjeuner' ? 0 : 1
|
|
|
|
// Check if a meal with the same date and type already exists
|
|
const existingMeal = await Meal.query()
|
|
.where({ date, type: mealType })
|
|
.preload('courses')
|
|
.first()
|
|
if (existingMeal) {
|
|
// Check if the existing meal has the same courses
|
|
const existingCourseNames = existingMeal.courses.map((course) => course.description)
|
|
const newCourseNames = courses.map((course) => course.description)
|
|
if (
|
|
existingCourseNames.length === newCourseNames.length &&
|
|
existingCourseNames.every((name) => newCourseNames.includes(name))
|
|
) {
|
|
return existingMeal
|
|
}
|
|
|
|
// If not, delete the existing meal (and its courses, thanks to cascade delete)
|
|
await existingMeal.delete()
|
|
}
|
|
|
|
// Create the new meal
|
|
const meal = await Meal.create({
|
|
date,
|
|
type: mealType,
|
|
})
|
|
await meal.related('courses').createMany(courses)
|
|
return meal
|
|
}
|
|
|
|
// GET /meals
|
|
public async index({ auth }: HttpContext) {
|
|
const meals = await Meal.query().orderBy('date', 'asc').preload('courses')
|
|
const data = meals.map(async (meal) => {
|
|
const isRegistered = await MealRegistration.query()
|
|
.where('meal_id', meal.id)
|
|
.where('user_id', auth.user!.id)
|
|
.first()
|
|
// Remove temporary registrations that are older than 5 minutes
|
|
if (isRegistered?.temporary) {
|
|
const oneHourAgo = DateTime.now().minus({ minutes: 5 })
|
|
if (isRegistered.createdAt < oneHourAgo) {
|
|
await isRegistered.delete()
|
|
return meal.serialize()
|
|
}
|
|
}
|
|
return {
|
|
...meal.serialize(),
|
|
isRegistered: !!isRegistered,
|
|
}
|
|
})
|
|
|
|
return Promise.all(data)
|
|
}
|
|
|
|
// POST /meals/:id
|
|
// TODO: Unregister from meal
|
|
public async registerForMeal({ params, auth, response, request }: HttpContext) {
|
|
const { username, password } = await request.validateUsing(credentialsValidator)
|
|
|
|
const meal = await Meal.find(params.id)
|
|
if (!meal) {
|
|
return response.notFound({ message: 'Meal not found' })
|
|
}
|
|
if (!meal.submittable) {
|
|
return response.badRequest({ message: 'Meal is not submittable' })
|
|
}
|
|
|
|
// Create a bullshit registration
|
|
// (to confirm to the user that the registration is in progress...)
|
|
// that will eventually be deleted if the registration fails (worker callback)
|
|
await MealRegistration.firstOrCreate({
|
|
mealId: meal.id,
|
|
userId: auth.user!.id,
|
|
temporary: true,
|
|
})
|
|
|
|
// Register the user for the meal
|
|
await redis.publish(
|
|
'jobs_queue',
|
|
JSON.stringify({
|
|
type: 4, // Submit meals
|
|
meal: {
|
|
date: DateTime.fromJSDate(meal.date).toISODate(),
|
|
meal_type: meal.type === 0 ? 'lunch' : 'dinner',
|
|
},
|
|
class_name: auth.user!.className,
|
|
user_id: auth.user!.id,
|
|
bj_username: username,
|
|
bj_password: password,
|
|
})
|
|
)
|
|
|
|
return { success: true, message: 'Registration in progress' }
|
|
}
|
|
}
|