import type { HttpContext } from '@adonisjs/core/http' import { existsSync, rmSync } from 'node:fs' import Computer from '#models/computer' import ComputerState from '#models/computer_state' import Component from '#models/component' import { storeComputerValidator } from '#validators/Computers/store' import { handleComponents } from '#services/component_service' import { handlePictures } from '#services/picture_service' import type { Image } from '#types/UItypes' import { updateComputerValidator } from '#validators/Computers/update' /** * Lucid's `serialize()` returns a loosely typed `ModelObject` * (`{ [key: string]: any }`). It is not structurally assignable to the * concrete prop shapes declared by the Vue pages (see * `inertia/pages/types/UITypes.ts`), which live in a separate TypeScript * project (the `inertia/**` tree is excluded from the backend tsconfig). * Since the controller fully controls which columns/relations are * preloaded and serialized, this cast documents that the runtime shape * matches what the page expects at this boundary. */ function asPageProp(value: unknown): T { return value as T } export default class ComputersController { async index({ auth, inertia }: HttpContext) { const user = await auth.authenticate() const computers = await Computer.query() .where('user_id', user.id) .preload('state') .preload('components') .preload('pictures', (picturesQuery) => { picturesQuery.where('order', 1) }) return inertia.render('computers/index', { computers: asPageProp(computers.map((c) => c.serialize())), }) } async create({ auth, inertia }: HttpContext) { const user = await auth.authenticate() const states = await ComputerState.all() const components = await Component.query() .where('userId', user.id) .whereNull('computerId') .preload('type') return inertia.render('computers/create', { components: asPageProp(components.map((c) => c.serialize())), states: asPageProp(states.map((s) => s.serialize())), }) } async store({ auth, request, response }: HttpContext) { const user = await auth.authenticate() const { pictures, componentsId, ...computerData } = await request.validateUsing(storeComputerValidator) await ComputerState.findOrFail(computerData.stateId) const computer = await Computer.create({ ...computerData, userId: user.id, }) await handleComponents(componentsId as number[], computer, user.id) await handlePictures(pictures as Image[], request, computer) return response.redirect('/computers') } async show({ auth, inertia, params }: HttpContext) { const user = await auth.authenticate() const computerId = params.id const computer = await Computer.query() .preload('state') .preload('components', (componentsQuery) => { componentsQuery.preload('type') }) .preload('pictures', (picturesQuery) => { picturesQuery.orderBy('order') }) .where('user_id', user.id) .andWhere('id', computerId) .firstOrFail() return inertia.render('computers/show', { computer: asPageProp({ ...computer.serialize(), shareToken: computer.shareToken }), }) } async edit({ params, inertia, auth }: HttpContext) { const user = await auth.authenticate() const states = await ComputerState.all() const computerId = params.id const computer = await Computer.query() .preload('state') .preload('components', (componentsQuery) => { componentsQuery.preload('type') }) .preload('pictures') .where('user_id', user.id) .andWhere('id', computerId) .firstOrFail() const availableComponents = await Component.query() .preload('type') .where('user_id', user.id) .whereNull('computer_id') .whereNotIn( 'id', computer.components.map((c) => c.id) ) return inertia.render('computers/edit', { computer: asPageProp(computer.serialize()), availableComponents: asPageProp(availableComponents.map((c) => c.serialize())), states: asPageProp(states.map((s) => s.serialize())), }) } async update({ auth, params, request, response }: HttpContext) { const user = await auth.authenticate() const computerId = params.id const { pictures, componentsId, ...computerData } = await request.validateUsing( updateComputerValidator ) const computer = await Computer.query() .where('user_id', user.id) .andWhere('id', computerId) .firstOrFail() computer.merge(computerData) await computer.save() await handleComponents(componentsId as number[], computer, user.id) await handlePictures(pictures as Image[], request, computer) return response.redirect(`/computers/${computer.id}`) } async destroy({ auth, params, response }: HttpContext) { const user = await auth.authenticate() const computerId = params.id const computer = await Computer.query() .where('user_id', user.id) .andWhere('id', computerId) .firstOrFail() const uploadsDir = `uploads/computer_${computer.id}` await computer.related('components').query().update({ computerId: null }) if (existsSync(uploadsDir)) { rmSync(uploadsDir, { recursive: true, force: true }) } await computer.delete() return response.redirect('/computers') } }