Compare commits
No commits in common. "06fc137057faaba37cc81675187130de181860b1" and "1082f29143cfe4fff62dbf4c220c904b4447e3b3" have entirely different histories.
06fc137057
...
1082f29143
11 changed files with 49 additions and 225 deletions
|
|
@ -3,7 +3,6 @@ import { ColleService } from '#services/colle_service'
|
||||||
import { createColleValidator, createUpcomingCollesValidator } from '#validators/colle'
|
import { createColleValidator, createUpcomingCollesValidator } from '#validators/colle'
|
||||||
import { inject } from '@adonisjs/core'
|
import { inject } from '@adonisjs/core'
|
||||||
import type { HttpContext } from '@adonisjs/core/http'
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
import redis from '@adonisjs/redis/services/main'
|
|
||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
|
|
@ -43,7 +42,6 @@ export default class CollesController {
|
||||||
.preload('examiner')
|
.preload('examiner')
|
||||||
.preload('subject')
|
.preload('subject')
|
||||||
.preload('room')
|
.preload('room')
|
||||||
.preload('attachments')
|
|
||||||
.first()
|
.first()
|
||||||
if (!colle) {
|
if (!colle) {
|
||||||
return response.notFound({ message: 'Colle not found' })
|
return response.notFound({ message: 'Colle not found' })
|
||||||
|
|
@ -59,39 +57,6 @@ export default class CollesController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async refresh({ params, response, auth }: HttpContext) {
|
|
||||||
const colleId = parseInt(params.colleId)
|
|
||||||
if (isNaN(colleId)) {
|
|
||||||
return response.badRequest({ message: 'Invalid colle ID' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the colle by ID
|
|
||||||
const colle = await Colle.query()
|
|
||||||
.where('id', colleId)
|
|
||||||
.whereHas('student', (query) => {
|
|
||||||
query.where('className', auth.user!.className)
|
|
||||||
})
|
|
||||||
.first()
|
|
||||||
if (!colle) {
|
|
||||||
return response.notFound({ message: 'Colle not found' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post Redis message to refresh the colle
|
|
||||||
await redis.publish(
|
|
||||||
'jobs_queue',
|
|
||||||
JSON.stringify({
|
|
||||||
type: 0, // Refresh colle
|
|
||||||
colle_id: colle.bjid,
|
|
||||||
colle_secret: colle.bjsecret,
|
|
||||||
class_name: auth.user!.className,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return response.ok({
|
|
||||||
success: true,
|
|
||||||
message: `Colle ${colleId} refresh request sent`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async create({ request, response }: HttpContext) {
|
async create({ request, response }: HttpContext) {
|
||||||
const { colle: payload, className } = await request.validateUsing(createColleValidator)
|
const { colle: payload, className } = await request.validateUsing(createColleValidator)
|
||||||
|
|
||||||
|
|
@ -128,54 +93,19 @@ export default class CollesController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the colle already exists
|
// Check if the colle already exists
|
||||||
const existing = await Colle.query()
|
const colle = await Colle.query()
|
||||||
.where('studentId', student.id)
|
.where('studentId', student.id)
|
||||||
.where('subjectId', subject.id)
|
.where('subjectId', subject.id)
|
||||||
.where('date', date.toISO())
|
.where('date', date.toISO())
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
// If it exists, update the existing colle
|
// If it exists, update the existing colle
|
||||||
if (existing) {
|
if (colle) {
|
||||||
// Merge the new data with the existing colle
|
Object.assign(colle, colleData)
|
||||||
Object.assign(existing, colleData)
|
return colle.save()
|
||||||
|
|
||||||
// Handle attachments if any
|
|
||||||
if (payload.attachments && payload.attachments.length > 0) {
|
|
||||||
// Retrieve existing attachments
|
|
||||||
const existingAttachments = await existing.related('attachments').query()
|
|
||||||
// Remove attachments that are not in the new payload
|
|
||||||
const existingAttachmentUrls = new Set(existingAttachments.map((a) => a.path))
|
|
||||||
for (const attachment of payload.attachments) {
|
|
||||||
if (!existingAttachmentUrls.has(attachment.url)) {
|
|
||||||
await existing.related('attachments').create({
|
|
||||||
name: attachment.name,
|
|
||||||
path: attachment.url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove attachments that are not in the new payload
|
|
||||||
for (const attachment of existingAttachments) {
|
|
||||||
if (!payload.attachments.some((a) => a.url === attachment.path)) {
|
|
||||||
await attachment.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return existing.save()
|
|
||||||
}
|
}
|
||||||
const colle = await Colle.create(colleData)
|
// Create the colle
|
||||||
|
return Colle.create(colleData)
|
||||||
// Handle attachments if any
|
|
||||||
if (payload.attachments && payload.attachments.length > 0) {
|
|
||||||
for (const attachment of payload.attachments) {
|
|
||||||
await colle.related('attachments').create({
|
|
||||||
path: attachment.url,
|
|
||||||
name: attachment.name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return colle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUpcoming({ request }: HttpContext) {
|
async createUpcoming({ request }: HttpContext) {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import type { HttpContext } from '@adonisjs/core/http'
|
|
||||||
import { inject } from '@adonisjs/core'
|
|
||||||
import { SubjectService } from '#services/subject_service'
|
|
||||||
|
|
||||||
@inject()
|
|
||||||
export default class SubjectsController {
|
|
||||||
constructor(private subjectService: SubjectService) {}
|
|
||||||
|
|
||||||
// GET /subjects
|
|
||||||
async index({ auth }: HttpContext) {
|
|
||||||
const data = await this.subjectService.getAll(auth.user!.className)
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +1,28 @@
|
||||||
import { SubjectService } from '#services/subject_service'
|
import User from '#models/user'
|
||||||
import { updateUserValidator } from '#validators/user'
|
import { createUserValidator } from '#validators/user'
|
||||||
import { inject } from '@adonisjs/core'
|
|
||||||
import type { HttpContext } from '@adonisjs/core/http'
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
|
import { cuid } from '@adonisjs/core/helpers'
|
||||||
|
|
||||||
@inject()
|
|
||||||
export default class UserController {
|
export default class UserController {
|
||||||
constructor(private subjectService: SubjectService) {}
|
|
||||||
|
|
||||||
// GET /users/@me
|
// GET /users/@me
|
||||||
async me({ auth }: HttpContext) {
|
async me({ auth }: HttpContext) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: auth.user,
|
||||||
...auth.user?.serialize(),
|
|
||||||
email: auth.user!.email || '',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /users/@me
|
// POST /users
|
||||||
// Update user preferences (for subjects)
|
async create({ request }: HttpContext) {
|
||||||
async update({ request, response, auth }: HttpContext) {
|
const payload = await request.validateUsing(createUserValidator)
|
||||||
const user = auth.user!
|
// Save avatar
|
||||||
const { preferences: data } = await request.validateUsing(updateUserValidator)
|
const avatar = `avatars/${cuid()}.${payload.avatar.extname}`
|
||||||
const preferences = user.extras?.preferences || []
|
await payload.avatar.moveToDisk(avatar)
|
||||||
|
// const avatar = await drive.use().getSignedUrl(key)
|
||||||
// Validate subject names
|
return User.create({
|
||||||
const validSubjects = await this.subjectService.getAll(user.className)
|
...payload,
|
||||||
for (const { name, emoji, color } of data) {
|
// TODO: No avatar for now!!
|
||||||
if (!validSubjects.includes(name)) {
|
// avatar,
|
||||||
return response.badRequest({
|
})
|
||||||
message: `Invalid subject name: ${name}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const existing = preferences.find((p: any) => p.name === name)
|
|
||||||
if (existing) {
|
|
||||||
// Update
|
|
||||||
existing.emoji = emoji
|
|
||||||
existing.color = color
|
|
||||||
} else {
|
|
||||||
// Create new preference
|
|
||||||
preferences.push({ name, emoji, color })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user.extras = {
|
|
||||||
...user.extras,
|
|
||||||
preferences,
|
|
||||||
}
|
|
||||||
await user.save()
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: user,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ export default class ColleAttachment extends BaseModel {
|
||||||
@column({ isPrimary: true })
|
@column({ isPrimary: true })
|
||||||
declare id: number
|
declare id: number
|
||||||
|
|
||||||
@column()
|
|
||||||
declare colleId: number
|
|
||||||
|
|
||||||
@column()
|
@column()
|
||||||
declare name: string
|
declare name: string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ export default class User extends BaseModel {
|
||||||
@column()
|
@column()
|
||||||
declare className: string
|
declare className: string
|
||||||
|
|
||||||
@column({ serializeAs: null })
|
@column({ serializeAs: null})
|
||||||
declare firstName: string
|
declare firstName: string
|
||||||
|
|
||||||
@column({ serializeAs: null })
|
@column({ serializeAs: null})
|
||||||
declare lastName: string
|
declare lastName: string
|
||||||
|
|
||||||
@computed()
|
@computed()
|
||||||
|
|
@ -25,14 +25,6 @@ export default class User extends BaseModel {
|
||||||
@column({ serializeAs: null })
|
@column({ serializeAs: null })
|
||||||
declare email: string
|
declare email: string
|
||||||
|
|
||||||
@column({ serializeAs: null })
|
|
||||||
declare extras: Record<string, any>
|
|
||||||
|
|
||||||
@computed()
|
|
||||||
get preferences(): { name: string; emoji: string; color: string }[] {
|
|
||||||
return this.extras?.preferences || []
|
|
||||||
}
|
|
||||||
|
|
||||||
@column.dateTime({ autoCreate: true })
|
@column.dateTime({ autoCreate: true })
|
||||||
declare createdAt: DateTime
|
declare createdAt: DateTime
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,8 @@ import Examiner from '#models/examiner'
|
||||||
import Room from '#models/room'
|
import Room from '#models/room'
|
||||||
import Subject from '#models/subject'
|
import Subject from '#models/subject'
|
||||||
import User from '#models/user'
|
import User from '#models/user'
|
||||||
import redis from '@adonisjs/redis/services/main'
|
|
||||||
|
|
||||||
export class ColleService {
|
export class ColleService {
|
||||||
async getHealthyUntil(className: string) {
|
|
||||||
const healtyUntil = await redis.get(`healthy_until_${className}`)
|
|
||||||
return new Date(healtyUntil || '')
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLastSync(className: string) {
|
|
||||||
const lastSync = await redis.get(`last_sync_${className}`)
|
|
||||||
return new Date(lastSync || '')
|
|
||||||
}
|
|
||||||
|
|
||||||
async getStudent(studentName: string, className: string) {
|
async getStudent(studentName: string, className: string) {
|
||||||
// Find or create a student by name
|
// Find or create a student by name
|
||||||
const { firstName, lastName } = this.splitNames(studentName)
|
const { firstName, lastName } = this.splitNames(studentName)
|
||||||
|
|
@ -75,7 +64,6 @@ export class ColleService {
|
||||||
.preload('examiner')
|
.preload('examiner')
|
||||||
.preload('subject')
|
.preload('subject')
|
||||||
.preload('room')
|
.preload('room')
|
||||||
.preload('attachments')
|
|
||||||
.where('date', '>=', startDate)
|
.where('date', '>=', startDate)
|
||||||
.where('date', '<=', endDate)
|
.where('date', '<=', endDate)
|
||||||
.whereHas('student', (query) => {
|
.whereHas('student', (query) => {
|
||||||
|
|
@ -86,11 +74,10 @@ export class ColleService {
|
||||||
.orderBy('date', 'asc')
|
.orderBy('date', 'asc')
|
||||||
|
|
||||||
const studentColles = await Colle.query()
|
const studentColles = await Colle.query()
|
||||||
.preload('student')
|
|
||||||
.preload('examiner')
|
.preload('examiner')
|
||||||
.preload('subject')
|
.preload('subject')
|
||||||
.preload('room')
|
.preload('room')
|
||||||
.preload('attachments')
|
.preload('student')
|
||||||
.where('date', '>=', startDate)
|
.where('date', '>=', startDate)
|
||||||
.where('date', '<=', endDate)
|
.where('date', '<=', endDate)
|
||||||
.where('studentId', student.id)
|
.where('studentId', student.id)
|
||||||
|
|
@ -103,8 +90,6 @@ export class ColleService {
|
||||||
classColles,
|
classColles,
|
||||||
studentColles,
|
studentColles,
|
||||||
favoriteColles,
|
favoriteColles,
|
||||||
healthyUntil: await this.getHealthyUntil(student.className),
|
|
||||||
lastSync: await this.getLastSync(student.className),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import Colle from '#models/colle'
|
|
||||||
import Subject from '#models/subject'
|
|
||||||
|
|
||||||
export class SubjectService {
|
|
||||||
async getAll(className: string): Promise<string[]> {
|
|
||||||
const subjectsIds = (
|
|
||||||
await Colle.query()
|
|
||||||
.distinct('subjectId')
|
|
||||||
.select('subjectId')
|
|
||||||
.whereHas('student', (query) => {
|
|
||||||
query.where('className', className)
|
|
||||||
})
|
|
||||||
).map((colle) => colle.subjectId)
|
|
||||||
|
|
||||||
const subjects = await Subject.query().whereIn('id', subjectsIds).select('name')
|
|
||||||
return subjects.map((subject) => subject.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -11,12 +11,7 @@ const colle = vine.object({
|
||||||
date: vine.date({ formats: ['iso8601'] }),
|
date: vine.date({ formats: ['iso8601'] }),
|
||||||
bjsecret: vine.string().optional(),
|
bjsecret: vine.string().optional(),
|
||||||
bjid: vine.string().optional(),
|
bjid: vine.string().optional(),
|
||||||
attachments: vine.array(
|
// TODO: Add attachments validation
|
||||||
vine.object({
|
|
||||||
url: vine.string(),
|
|
||||||
name: vine.string().maxLength(255),
|
|
||||||
})
|
|
||||||
).optional(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const className = vine.string()
|
const className = vine.string()
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import vine from '@vinejs/vine'
|
import vine from '@vinejs/vine'
|
||||||
|
|
||||||
export const updateUserValidator = vine.compile(
|
export const createUserValidator = vine.compile(
|
||||||
vine.object({
|
vine.object({
|
||||||
preferences: vine.array(
|
firstName: vine.string().minLength(2).maxLength(50),
|
||||||
vine.object({
|
lastName: vine.string().minLength(2).maxLength(50),
|
||||||
name: vine.string(),
|
className: vine.string().minLength(2).maxLength(10),
|
||||||
emoji: vine.string().maxLength(12),
|
avatar: vine.file(),
|
||||||
color: vine.string().maxLength(12),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { BaseSchema } from '@adonisjs/lucid/schema'
|
|
||||||
|
|
||||||
export default class extends BaseSchema {
|
|
||||||
protected tableName = 'users'
|
|
||||||
|
|
||||||
async up() {
|
|
||||||
this.schema.alterTable(this.tableName, (table) => {
|
|
||||||
// Adding a JSON column for extras (can hold flexible data)
|
|
||||||
table.jsonb('extras').nullable().after('updated_at')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async down() {
|
|
||||||
this.schema.alterTable(this.tableName, (table) => {
|
|
||||||
table.dropColumn('extras')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,13 +8,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import router from '@adonisjs/core/services/router'
|
import router from '@adonisjs/core/services/router'
|
||||||
// import transmit from '@adonisjs/transmit/services/main'
|
import transmit from '@adonisjs/transmit/services/main'
|
||||||
import { authThrottle } from './limiters.js'
|
import { authThrottle } from './limiters.js'
|
||||||
import { throttle } from './limiter.js'
|
import { throttle } from './limiter.js'
|
||||||
import { middleware } from './kernel.js'
|
import { middleware } from './kernel.js'
|
||||||
|
|
||||||
// TODO: Magic link login
|
transmit.registerRoutes()
|
||||||
// transmit.registerRoutes()
|
|
||||||
|
|
||||||
const AuthController = () => import('#controllers/auth_controller')
|
const AuthController = () => import('#controllers/auth_controller')
|
||||||
|
|
||||||
|
|
@ -30,19 +29,30 @@ router.group(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const UserController = () => import('#controllers/user_controller')
|
const UserController = () => import('#controllers/user_controller')
|
||||||
router.get('/users/@me', [UserController, 'me']).use(middleware.auth())
|
|
||||||
router.post('/users/@me', [UserController, 'update']).use(middleware.auth())
|
|
||||||
|
|
||||||
const SubjectsController = () => import('#controllers/subjects_controller')
|
router.get('/users/@me', [UserController, 'me']).use(middleware.auth())
|
||||||
router.get('/subjects', [SubjectsController, 'index']).use(middleware.auth())
|
|
||||||
|
// TEST ROUTE
|
||||||
|
import redis from '@adonisjs/redis/services/main'
|
||||||
|
|
||||||
|
router.get('/', async () => {
|
||||||
|
await redis.publish("jobs_queue", JSON.stringify({
|
||||||
|
type: 1,
|
||||||
|
date: "09/12/2024"
|
||||||
|
}))
|
||||||
|
return { message: 'Hello, world!' }
|
||||||
|
})
|
||||||
|
// END TEST ROUTE
|
||||||
|
|
||||||
const CollesController = () => import('#controllers/colles_controller')
|
const CollesController = () => import('#controllers/colles_controller')
|
||||||
router.group(() => {
|
router.group(() => {
|
||||||
// TODO: PRIVATE ROUTES
|
// TODO: PRIVATE ROUTES
|
||||||
router.post('/', [CollesController, 'create'])
|
router.post('/', [CollesController, 'create'])
|
||||||
router.post('/upcoming', [CollesController, 'createUpcoming'])
|
router.post('/upcoming', [CollesController, 'createUpcoming'])
|
||||||
router.post('/:colleId/refresh', [CollesController, 'refresh']).use(middleware.auth())
|
|
||||||
router.get('/', [CollesController, 'index']).use(middleware.auth())
|
router.get('/', [CollesController, 'index']).use(middleware.auth())
|
||||||
router.get('/:colleId', [CollesController, 'show']).use(middleware.auth())
|
router.get('/:colleId', [CollesController, 'show']).use(middleware.auth())
|
||||||
|
// router.get('/colles/:id', 'CollesController.show')
|
||||||
|
// router.put('/colles/:id', 'CollesController.update')
|
||||||
|
// router.delete('/colles/:id', 'CollesController.delete')
|
||||||
}
|
}
|
||||||
).prefix('/colles')
|
).prefix('/colles')
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue