CoolFace
Apppublic

anas044/attendance-nodejs

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
controllers.js1324 linesDownload Raw Back to controllers
1const axios = require("axios")2const path = require('path')3const fs = require('fs')4const pool = require("../pg/pg");5const bcrypt = require("bcrypt");6const { generateAccessToken, generateRefreshToken, generateJitsiToken } = require("../utils/tokens");7const { generateRoomId } = require("../utils/functions");8const UPLOADS_DIR = path.join(__dirname, '..', 'uploads');9const { Resend } = require('resend');10const resend = new Resend(process.env.RESENDAPIKEY);11 12exports.signup = async (req, res) => {13    try {14        const { email, firstName, lastName, password } = req.body;15 16        if (!email || !firstName || !lastName || !password) {17            return res.status(400).json({ 18                message: "All fields are required: email, firstName, lastName, password" 19            });20        }21 22        const existing = await pool.query(23            "SELECT * FROM users WHERE email = $1",24            [email]25        );26 27        if (existing.rows.length > 0) {28            return res.status(400).json({ 29                message: "Email already in use. Please login or use a different email." 30            });31        }32 33        const hashedPassword = await bcrypt.hash(password, 10);34        35        const result = await pool.query(36            `INSERT INTO users (email, first_name, last_name, password_hash, role)37             VALUES ($1, $2, $3, $4, $5) 38             RETURNING id, email, first_name, last_name, role, created_at`,39            [email, firstName, lastName, hashedPassword, 'student'] 40        );41 42        const newUser = result.rows[0];43 44 45 46        return res.status(201).json({47            success: true,48            message: "User registered successfully",49        });50 51    } catch (err) {52        console.error('Signup error:', err);53        54        if (err.code === '23505') { 55            return res.status(400).json({ 56                message: "Email already exists. Please use a different email." 57            });58        }59        60        if (err.code === '22001') { 61            return res.status(400).json({ 62                message: "One or more fields are too long." 63            });64        }65        66        return res.status(500).json({ 67            message: "Server error during registration. Please try again." 68        });69    }70};71 72exports.login = async (req, res) => {73    try {74        const { email, password } = req.body;75 76        const result = await pool.query(77            "SELECT * FROM users WHERE email = $1",78            [email]79        );80 81        if (result.rows.length === 0) {82            return res.status(400).json({ message: "Invalid credentials" });83        }84 85        const user = result.rows[0];86 87        const valid = await bcrypt.compare(password, user.password_hash);88        if (!valid) return res.status(400).json({ message: "Invalid credentials" });89 90        const payload = { id: user.id, email: user.email, role: user.role };91        const accessToken = generateAccessToken(payload);92        const refreshToken = generateRefreshToken(payload);93 94        await pool.query("insert into refreshtokens (token, user_id) VALUES ($1,$2)", [95            refreshToken,96            user.id,97        ]);98 99        const seid = {100            user_id: user.id,101            firstName: user.first_name,102            lastName: user.last_name,103            profile_image: user.profile_image,104            role: user.role105 106        }107 108        res.cookie("refresh", refreshToken, {109            httpOnly: true,110            secure: true,111            sameSite: "strict",112            path: '/api/refresh',113            maxAge: 7 * 24 * 60 * 60 * 1000,114        });115 116        res.cookie("access", accessToken, {117            httpOnly: true,118            secure: true,119            sameSite: "lax",120            maxAge: 15 * 60 * 1000,121        });122 123        res.cookie('details', JSON.stringify(seid), {124            httpOnly: false,125            secure: false,126            sameSite: 'strict',127            path: '/',128            maxAge: undefined129        });130 131        return res.status(200).json({132            message: "Login successful",133 134 135        });136 137    } catch (err) {138        console.log(err);139        res.status(500).json({ message: err.message });140    }141};142 143exports.refreshToken = async (req, res) => {144    const jwt = req.cookies.refresh;145    if (!jwt)146        return res.status(401).json({ message: "No refresh token provided" });147 148    try {149        const decoded = require("jsonwebtoken").verify(150            jwt,151            process.env.REFRESH_TOKEN_SECRET152        );153 154        const accessToken = generateAccessToken({155            id: decoded.id,156            email: decoded.email,157            role: decoded.role,158        });159 160        res.cookie("access", accessToken, {161            httpOnly: true,162            secure: true,163            sameSite: "lax",164            maxAge: 15 * 60 * 1000,165        });166 167        return res.status(200).json({ message: 'access token sent' })168    } catch (err) {169        return res.status(403).json({ message: "Invalid refresh token" });170    }171};172 173exports.checkFaceChange = async (req, res) => {174    try {175        const check = await pool.query('select face_change from users where id = $1', [req.user.id])176        const check2 = await pool.query(`select * from face_change where status = 'pending' and student_id = $1 `, [req.user.id])177        return res.status(200).json({ message: check.rows[0].face_change, message2: check2.rowCount > 0 ? true : false });178 179    } catch (error) {180      console.log(error)181        return res.status(500).json({ message: "server error" });182 183    }184}185 186exports.requestFaceChange = async (req, res) => {187    try {188        await pool.query('insert into face_change (student_id) values ($1)', [req.user.id])189        return res.status(200).json({ message: 'Success' });190 191    } catch (error) {192        return res.status(500).json({ message: "Server error" });193 194    }195}196exports.getRequestFaceChange = async (req, res) => {197    try {198        const { course_id } = req.body199        if (req.user.role !== 'teacher') {200            return res.status(401).json({ message: 'unauthorized' })201 202        }203        const ress = await pool.query('select fc.*, u.first_name, u.last_name from face_change fc left join users u on fc.student_id = u.id left join student_courses sc on u.id = sc.student_id left join teacher_courses tc on sc.course_id = tc.course_id where tc.teacher_id = $1 and tc.course_id = $2', [req.user.id, parseInt(course_id, 10)])204        return res.status(200).json({ message: ress.rows[0] });205 206    } catch (error) {207        console.log(error)208        return res.status(500).json({ message: "Server error" });209 210    }211}212exports.manageRequestFaceChange = async (req, res) => {213    try {214        const { ans, rid, student_id } = req.body215 216        if (req.user.role !== 'teacher') {217            return res.status(401).json({ message: 'unauthorized' })218 219        }220        await pool.query(`update face_change set status = $1 where id = $2`, [ans ? 'approved' : 'declined', rid])221        await pool.query('update users set face_change = $1 where id = $2 ', [ans ? 'true' : 'false', student_id])222        return res.status(200).json({ message: 'Success' });223 224    } catch (error) {225        return res.status(500).json({ message: "Server error" });226 227    }228}229 230exports.processFrame = async (req, res) => {231    try {232        const { frame } = req.body;233 234        if (!frame) {235            return res.status(400).json({ error: "Frame not provided" });236        }237 238        const check = await pool.query('select face_change from users where id = $1', [req.user.id])239        if (!check.rows[0].face_change) {240            return res.status(500).json({ message: 'you do not have access to change your face encoding' });241        }242        const base64Data = frame.replace(/^data:image\/\w+;base64,/, "");243 244        const result = await axios.post(245            "https://anas044-attendance.hf.space/encode_face",246            { image: base64Data },247            { headers: { "Content-Type": "application/json" } }248        );249 250        user_id = req.user.id251 252        await pool.query("update users set face_encoding= $1 where id = $2", [253            result.data.encoding,254            user_id,255        ]);256        await pool.query('update users set face_change = false where id = $1', [req.user.id])257 258        return res.json({ message: 'encoding saved' });259 260 261    } catch (err) {262        console.log(err)263        const status = err.response?.status || 500;264        const message = err.response?.data?.detail || err.response?.data?.message || "Internal server error";265 266        return res.status(status).json({ message: message });267    }268 269}270 271exports.saveCourse = async (req, res) => {272    try {273        const { name, schedule } = req.body274 275        const teacher_id = req.user.id276        const save = await pool.query('insert into courses (name, schedule) values ($1 ,$2) returning *', [name, schedule])277        await pool.query('insert into teacher_courses (teacher_id, course_id) values ($1 ,$2)', [teacher_id, save.rows[0].id])278 279        return res.status(200).json({ message: 'saved' })280    } catch (error) {281        return res.status(500).json({ message: 'server error' });282 283    }284}285 286exports.updateCourse = async (req, res) => {287    try {288        const { id, name, schedule } = req.body289 290        const teacher_id = req.user.id291 292        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, id])293 294        if (check.rowCount === 0) {295            return res.status(401).json({ message: 'unauthorized' })296        }297 298        await pool.query('update courses set name = $1, schedule = $2 where id = $3', [name, schedule, id])299        return res.status(200).json({ message: 'updated' })300    } catch (error) {301        console.log(error)302        return res.status(500).json({ message: 'server error' });303 304    }305}306 307exports.deleteCourse = async (req, res) => {308    try {309        const { id } = req.body310 311        const teacher_id = req.user.id312 313        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, id])314 315        if (check.rowCount === 0) {316            return res.status(401).json({ message: 'unauthorized' })317        }318 319        await pool.query('delete from courses where id = $1', [id])320        return res.status(200).json({ message: 'updated' })321    } catch (error) {322        console.log(error)323        return res.status(500).json({ message: 'server error' });324 325    }326}327 328exports.addStudentToCourse = async (req, res) => {329    try {330        const { student_id, course_id } = req.body331 332        const teacher_id = req.user.id333 334        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, course_id])335 336        if (check.rowCount === 0) {337            return res.status(401).json({ message: 'unauthorized' })338        }339 340        await pool.query('insert into student_courses (student_id, course_id) values ($1 ,$2)', [student_id, course_id])341        return res.status(200).json({ message: 'saved' })342    } catch (error) {343 344        return res.status(500).json({ message: 'server error' });345 346    }347}348 349exports.getCourseStudents = async (req, res) => {350    try {351        const { id } = req.body352        const teacher_id = req.user.id353 354        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, id])355 356        if (check.rowCount === 0) {357            return res.status(401).json({ message: 'unauthorized' })358        }359        const students = await pool.query('select sc.*, u.first_name, u.last_name, u.profile_image from student_courses sc left join users u on sc.student_id = u.id where course_id = $1', [id])360        return res.status(200).json({ message: students.rows })361    } catch (error) {362        console.log(error)363        return res.status(500).json({ message: 'server error' });364 365    }366}367 368exports.getCourses = async (req, res) => {369    try {370        const teacher_id = req.user.id371 372        const result = await pool.query(373            `SELECT c.*374             FROM teacher_courses tc375             JOIN courses c ON tc.course_id = c.id376             WHERE tc.teacher_id = $1`,377            [teacher_id]378        ); return res.status(200).json({ message: result.rows })379    } catch (error) {380 381        return res.status(500).json({ message: 'server error' });382 383    }384}385 386exports.removeStudentFromCourse = async (req, res) => {387    try {388        const { student_id, course_id } = req.body389 390        const teacher_id = req.user.id391 392        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, course_id])393 394        if (check.rowCount === 0) {395            return res.status(401).json({ message: 'unauthorized' })396        }397        await pool.query('delete from student_courses where student_id = $1 and course_id = $2', [student_id, course_id])398        await pool.query('delete from courses_registrations where student_id = $1 and course_id = $2', [student_id, course_id])399 400        return res.status(200).json({ message: 'updated' })401    } catch (error) {402 403        return res.status(500).json({ message: 'server error' });404 405    }406}407 408exports.getStudentCourses = async (req, res) => {409    try {410        const id = req.user.id411        const courses = await pool.query('select c.* from courses c JOIN student_courses sc ON sc.course_id = c.id WHERE sc.student_id = $1 ', [id])412        console.log(courses.rows)413        return res.status(200).json({ message: courses.rows })414    } catch (error) {415        return res.status(500).json({ message: 'server error' });416 417    }418}419 420exports.getStudentSessions = async (req, res) => {421    try {422        const { course_id } = req.body423 424        const check = await pool.query('select * from student_courses where course_id = $1 and student_id = $2', [course_id, req.user.id])425        if (check.rowCount === 0) {426            return res.status(401).json({ message: 'unauthorized' })427        }428        const sessions = await pool.query('select * from sessions where class_id = $1', [course_id])429        return res.status(200).json({ message: sessions.rowCount > 0 ? sessions.rows : 'no sessions' })430    } catch (error) {431        console.log(error)432        return res.status(500).json({ message: 'server error' });433 434    }435}436 437exports.getSessions = async (req, res) => {438    try {439 440        const { course_id } = req.body441 442        const teacher_id = req.user.id443 444        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, course_id])445 446        if (check.rowCount === 0) {447            return res.status(401).json({ message: 'unauthorized' })448        }449 450        const sessions = await pool.query('select * from sessions where class_id = $1', [course_id])451        return res.status(200).json({ message: sessions.rowCount > 0 ? sessions.rows : 'no sessions' })452 453    } catch (error) {454        return res.status(500).json({ message: 'server error' });455 456    }457}458 459exports.getStudentAttendance = async (req, res) => {460    try {461        const { student_id, session_id } = req.body;462 463        console.log(req.body)464 465        const course = await pool.query('select * from sessions where id = $1', [session_id])466 467        const teacher_id = req.user.id468 469        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, course.rows[0].class_id])470 471        if (check.rowCount === 0) {472            return res.status(401).json({ message: 'unauthorized' })473        }474        const user = await pool.query('select first_name, last_name from users where id = $1', [student_id])475        console.log(user.rows[0])476        const attendance = await pool.query('select a.*, u.first_name, u.last_name from attendance a left join users u on a.student_id = u.id where session_id = $1 and student_id = $2', [session_id, student_id])477        return res.status(200).json({ message: attendance.rowCount > 0 ? attendance.rows : 'Absent', fullName: user.rows[0] })478 479    } catch (error) {480        console.log(error)481        return res.status(500).json({ message: 'server error' });482 483    }484}485 486exports.deleteSession = async (req, res) => {487    try {488 489        const { session_id } = req.body490 491        const course = await pool.query('select class_id from sessions where id =$1', [session_id])492 493        const teacher_id = req.user.id494 495        const check = await pool.query('select * from teacher_courses where teacher_id = $1 and course_id = $2', [teacher_id, course.rows[0].class_id])496 497        if (check.rowCount === 0) {498            return res.status(401).json({ message: 'unauthorized' })499        }500 501        const sessions = await pool.query('delete from sessions where id = $1', [session_id])502        return res.status(200).json({ message: 'Deleted' })503 504    } catch (error) {505        return res.status(500).json({ message: 'server error' });506 507    }508}509exports.createSession = async (req, res) => {510    try {511        const {512            class_id,513            room_name,514            scheduled_time,515            end_scheduled_time516        } = req.body;517 518        const teacher_id = req.user.id;519        const teacherCheck = await pool.query(520            `SELECT * FROM teacher_courses 521             WHERE teacher_id = $1 AND course_id = $2`,522            [teacher_id, class_id]523        );524 525        if (teacherCheck.rowCount === 0) {526            return res.status(403).json({527                message: 'You are not authorized to create sessions for this course'528            });529        }530 531        const courseCheck = await pool.query(532            `SELECT name FROM courses WHERE id = $1`,533            [class_id]534        );535 536        if (courseCheck.rowCount === 0) {537            return res.status(404).json({538                message: 'Course not found'539            });540        }541        const courseName = courseCheck.rows[0].name;542 543        const roomId = generateRoomId(class_id, teacher_id);544 545 546 547        const defaultSettings = {548            enable_waiting_room: true,549            allow_recordings: false,550            enable_chat: true,551            enable_screen_sharing: false,552            start_with_audio_muted: true,553            start_with_video_muted: false,554            require_display_name: true,555            enable_close_page: false556        };557 558        const sessionSettings = {559            ...defaultSettings,560        };561 562 563 564        const sessionData = {565            class_id,566            teacher_id,567            room_name,568            jitsi_room_id: roomId,569            start_time: scheduled_time || new Date(),570            end_time: end_scheduled_time,571            settings: JSON.stringify(sessionSettings),572            is_active: false,573            session_status: 'scheduled',574            end_time: null575        };576 577        const insertQuery = `578            INSERT INTO sessions (579                class_id, teacher_id, room_name, jitsi_room_id, 580                start_time, settings, is_active, session_status, end_time581            ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) 582            RETURNING *583        `;584 585        const sessionResult = await pool.query(insertQuery, [586            sessionData.class_id,587            sessionData.teacher_id,588            sessionData.room_name,589            sessionData.jitsi_room_id,590            sessionData.start_time,591            sessionData.settings,592            sessionData.is_active,593            sessionData.session_status,594            sessionData.end_time595 596 597        ]);598        const createdSession = sessionResult.rows[0];599 600        const response = {601            message: 'Session created successfully',602 603 604        };605 606 607        return res.status(201).json(response);608 609 610    } catch (error) {611        console.log(error)612 613        res.status(500).json({ message: 'server error' })614    }615}616 617const jwt = require('jsonwebtoken');618const { transporter } = require("../utils/mail");619 620exports.getSessionToken = async (req, res) => {621    try {622        const { session_id } = req.body;623        console.log('Getting session token for session:', session_id);624 625        // Get session details626        const room_id = await pool.query(627            'SELECT jitsi_room_id, is_active, start_time, end_time FROM sessions WHERE id = $1',628            [session_id]629        );630 631        if (room_id.rowCount === 0) {632            return res.status(404).json({ message: 'no session with that id' })633        }634 635        const session = room_id.rows[0];636 637        if (!session.is_active) {638            console.log('Session not active:', session.is_active);639            return res.status(400).json({ message: 'session not active' })640        }641 642 643 644        const get = await pool.query(645            'SELECT * FROM session_tokens WHERE session_id = $1 AND user_id = $2',646            [session_id, req.user.id]647        );648 649        let token;650 651        if (get.rowCount === 0) {652            return res.status(401).json({ message: 'verify your face' })653        } else {654            const existingToken = get.rows[0].token;655 656            try {657                const decoded = jwt.decode(existingToken);658 659                if (!decoded) {660                    return res.status(401).json({ message: 'verify your face' })661                }662 663                const currentTime = Math.floor(Date.now() / 1000);664 665                if (decoded.exp && decoded.exp < currentTime) {666 667                    await pool.query(668                        'DELETE FROM session_tokens WHERE session_id = $1 AND user_id = $2',669                        [session_id, req.user.id]670                    );671 672                    return res.status(401).json({ message: 'verify your face' })673                } else {674                    token = existingToken;675                    const expiresIn = decoded.exp ? decoded.exp - currentTime : null;676                }677            } catch (decodeError) {678                return res.status(401).json({ message: 'verify your face' })679            }680        }681 682        return res.status(200).json({683            message: token,684            room_name: session.jitsi_room_id685        });686 687    } catch (error) {688        res.status(500).json({ message: 'server error' })689    }690}691 692exports.verifyFace = async (req, res) => {693    try {694        const { frame } = req.body;695 696        if (!frame) {697            return res.status(400).json({ error: "Frame not provided" });698        }699 700        const base64Data = frame.replace(/^data:image\/\w+;base64,/, "");701        user_id = req.user.id702 703        const check = await pool.query("select face_encoding from users where id = $1", [704            user_id,705        ]);706        if (check.rows[0].face_encoding === null || !check.rows[0].face_encoding) {707            return res.status(500).json({ message: 'no face encoding saved' })708 709        }710 711        const result = await axios.post(712            "https://anas044-attendance.hf.space/verify_face",713            { image: base64Data, encoding: check.rows[0].face_encoding },714            { headers: { "Content-Type": "application/json" } }715        );716        if (result.data.verified) {717            const { session_id } = req.body718            console.log(session_id)719            const room_id = await pool.query('select jitsi_room_id, is_active from sessions where id = $1', [session_id])720 721            if (room_id.rowCount === 0) {722                return res.status(404).json({ message: 'no session with that id' })723            }724 725            if (!room_id.rows[0].is_active) {726                console.log(room_id.rows[0].isactive)727                return res.status(400).json({ message: 'session not active' })728 729            }730 731            let isTeacher = false;732            let room_name;733 734            const role = req.user.role;735            if (role === 'teacher') {736                const teacherCheck = await pool.query(737                    'SELECT * FROM sessions WHERE id = $1 AND teacher_id = $2',738                    [session_id, req.user.id]739                );740 741                isTeacher = teacherCheck.rowCount > 0;742 743            }744            token = generateJitsiToken(req.user, room_id.rows[0].jitsi_room_id, isTeacher)745            await pool.query('insert into session_tokens (session_id, user_id, token) values ($1, $2, $3)', [session_id, req.user.id, token])746        }747 748 749        return res.status(200).json({ message: result.data.verified ? 'verified' : 'not verified' });750 751 752    } catch (err) {753        console.log(err)754        const status = err.response?.status || 500;755        const message = err.response?.data?.detail || err.response?.data?.message || "Internal server error";756 757        return res.status(status).json({ message: message });758    }759 760}761exports.startSession = async (req, res) => {762    try {763        const { session_id } = req.body764 765        const teacherCheck = await pool.query(766            `SELECT s.* FROM sessions s767                 JOIN teacher_courses tc ON s.class_id = tc.course_id768                 WHERE s.id = $1 AND tc.teacher_id = $2`,769            [session_id, req.user.id]770        );771        if (teacherCheck.rowCount === 0) {772            return res.status(401).json({ message: 'not authorized' })773        };774 775        await pool.query(`update sessions set session_status = 'active', is_active = true where id = $1`, [session_id])776        return res.status(200).json({ message: 'updated' })777 778    } catch (error) {779        console.log(error)780        res.status(500).json({ message: 'server error' })781 782    }783}784 785exports.endSession = async (req, res) => {786    try {787        const { session_id } = req.body788 789        const teacherCheck = await pool.query(790            `SELECT s.* FROM sessions s791                 JOIN teacher_courses tc ON s.class_id = tc.course_id792                 WHERE s.id = $1 AND tc.teacher_id = $2`,793            [session_id, req.user.id]794        );795        if (teacherCheck.rowCount === 0) {796            return res.status(401).json({ message: 'not authorized' })797        };798 799        await pool.query(`update sessions set session_status = 'ended', is_active = false where id = $1`, [session_id])800        return res.status(200).json({ message: 'updated' })801 802    } catch (error) {803        console.log(error)804        res.status(500).json({ message: 'server error' })805 806    }807}808 809exports.courseRegister = async (req, res) => {810    try {811        const { course_id } = req.body812        const user_id = req.user.id813 814        await pool.query('insert into courses_registrations (course_id, student_id) values ($1,$2)', [course_id, user_id])815        return res.status(200).json({ message: 'saved' })816    } catch (error) {817        res.status(500).json({ message: 'server error' })818 819    }820}821 822exports.courseRegistrRes = async (req, res) => {823    try {824        const { request_id, isAccepted } = req.body825        const user_id = req.user.id826        console.log(req.body)827 828        const check = await pool.query('select tc.* from teacher_courses tc JOIN courses_registrations cr ON tc.course_id = cr.course_id where tc.teacher_id = $1', [user_id])829        if (check.rowCount === 0) {830            return res.status(401).json({ message: 'not authorized' })831        }832 833        const save = await pool.query('update courses_registrations set status = $1 where id = $2 returning *', [isAccepted ? 'approved' : 'declined', request_id])834 835        if (isAccepted) {836            await pool.query('insert into student_courses (student_id, course_id) values ($1 ,$2)', [save.rows[0].student_id, save.rows[0].course_id])837        }838        return res.status(200).json({ message: 'saved' })839    } catch (error) {840        console.log(error)841        res.status(500).json({ message: 'server error' })842 843    }844}845 846exports.GetcourseRegistrRes = async (req, res) => {847    try {848        const user_id = req.user.id849 850        const check = await pool.query('select  cr.id, c.name, u.first_name, u.last_name, cr.student_id from teacher_courses tc JOIN courses_registrations cr ON tc.course_id = cr.course_id join courses c on c.id = cr.course_id left join users u on cr.student_id = u.id where tc.teacher_id = $1 and cr.status =$2', [user_id, 'pending'])851        // if (check.rowCount === 0) {852        //     return res.status(401).json({ message: 'not authorized' })853        // }854 855        // const save = await pool.query('update courses_registrations set status = $3 where id = $4', [isAccepted ? 'approved' : 'declined', user_id])856        // if (isAccepted) {857        //     await pool.query('insert into student_courses (student_id, course_id) values ($1 ,$2)', [save.rows[0].student_id, save.rows[0].course_id])858        // }859        return res.status(200).json({ message: check.rows })860    } catch (error) {861        res.status(500).json({ message: 'server error' })862 863    }864}865 866 867exports.getAvailableCourses = async (req, res) => {868    try {869        const user_id = req.user.id870 871        const result = await pool.query(872            `SELECT c.*, cr.status from courses c left join courses_registrations cr on c.id = cr.course_id and cr.student_id = $1 where c.id not in (select course_id from student_courses where student_id = $1);`, [user_id]);873        return res.status(200).json({ message: result.rows })874    } catch (error) {875        console.log(error)876        return res.status(500).json({ message: 'server error' });877 878    }879}880 881exports.setAttendance = async (req, res) => {882    try {883        const { session_id, joined_at, left_at } = req.body884        if (req.user.role === 'teacher') {885            return res.status(200).json({ message: 'no need' })886        }887        const user_id = req.user.id888        const check = await pool.query('select * from session_tokens where user_id = $1 and session_id = $2', [user_id, session_id])889 890        if (check.rowCount < 0) {891            return res.status(401).json({ message: 'did not attend the session' })892        }893        await pool.query('insert into attendance (session_id, student_id, joined_at, left_at) values ($1,$2, $3, $4)', [session_id, user_id, joined_at, left_at])894        res.status(200).json({ message: 'saved' })895    } catch (error) {896        return res.status(500).json({ message: 'server error' });897 898    }899}900 901 902 903exports.getProfile = async (req, res) => {904    try {905        const userId = req.user.id;906 907        const result = await pool.query(908            `SELECT 909                id, 910                email, 911                first_name, 912                last_name, 913                profile_image914             FROM users 915             WHERE id = $1`,916            [userId]917        );918 919        if (result.rowCount === 0) {920            return res.status(404).json({ message: 'User not found' });921        }922 923        res.status(200).json({ message: result.rows[0] });924    } catch (error) {925        console.error('Error fetching profile:', error);926        res.status(500).json({ message: 'Server error' });927    }928};929 930exports.updateProfile = async (req, res) => {931    try {932        const userId = req.user.id;933        const { email, first_name, last_name, phone_number } = req.body;934 935 936 937        if (email) {938            const emailCheck = await pool.query(939                'SELECT id FROM users WHERE email = $1 AND id != $2',940                [email, userId]941            );942            if (emailCheck.rowCount > 0) {943                return res.status(400).json({ message: 'Email already registered' });944            }945        }946 947        const result = await pool.query(948            `UPDATE users 949             SET 950                email = COALESCE($1, email),951                first_name = COALESCE($2, first_name),952                last_name = COALESCE($3, last_name)953             WHERE id = $4954             RETURNING id, email, first_name, last_name`,955            [email, first_name, last_name, userId]956        );957 958        const user = result.rows[0];959 960        const seid = {961            user_id: user.id,962            firstName: user.first_name,963            lastName: user.last_name,964            profile_image: user.profile_image,965            role: user.role966 967        }968 969 970        res.clearCookie('details')971        res.cookie('details', JSON.stringify(seid), {972            httpOnly: false,973            secure: false,974            sameSite: 'strict',975            path: '/',976            maxAge: undefined977        });978 979        return res.status(200).json({980            message: 'Profile updated successfully',981            user: result.rows[0]982        });983    } catch (error) {984        console.error('Error updating profile:', error);985        res.status(500).json({ message: 'Server error' });986    }987};988 989exports.changePassword = async (req, res) => {990    try {991        const userId = req.user.id;992        const { current_password, new_password, confirm_password } = req.body;993 994        if (!current_password || !new_password || !confirm_password) {995            return res.status(400).json({ message: 'All password fields are required' });996        }997 998        if (new_password !== confirm_password) {999            return res.status(400).json({ message: 'New passwords do not match' });1000        }1001 1002        if (new_password.length < 6) {1003            return res.status(400).json({ message: 'Password must be at least 6 characters long' });1004        }1005 1006        const userResult = await pool.query(1007            'SELECT password_hash FROM users WHERE id = $1',1008            [userId]1009        );1010 1011        if (userResult.rowCount === 0) {1012            return res.status(404).json({ message: 'User not found' });1013        }1014 1015        const currentHashedPassword = userResult.rows[0].password_hash;1016 1017        const isPasswordValid = await bcrypt.compare(current_password, currentHashedPassword);1018        if (!isPasswordValid) {1019            return res.status(400).json({ message: 'Current password is incorrect' });1020        }1021 1022        const saltRounds = 10;1023        const newHashedPassword = await bcrypt.hash(new_password, saltRounds);1024 1025        await pool.query(1026            'UPDATE users SET password_hash = $1 WHERE id = $2',1027            [newHashedPassword, userId]1028        );1029 1030        res.status(200).json({ message: 'Password changed successfully' });1031    } catch (error) {1032        console.error('Error changing password:', error);1033        res.status(500).json({ message: 'Server error' });1034    }1035};1036 1037exports.uploadProfileImage = async (req, res) => {1038    try {1039        const userId = req.user.id;1040 1041        if (!req.file) {1042            return res.status(400).json({ message: 'No image uploaded' });1043        }1044 1045        const imageUrl = `/uploads/${req.file.filename}`;1046 1047        const update = await pool.query(1048            'UPDATE users SET profile_image = $1 WHERE id = $2 returning *',1049            [imageUrl, userId]1050        );1051 1052        const user = update.rows[0];1053 1054        const seid = {1055            user_id: user.id,1056            firstName: user.first_name,1057            lastName: user.last_name,1058            profile_image: user.profile_image,1059            role: user.role1060 1061        }1062 1063 1064        res.clearCookie('details')1065        res.cookie('details', JSON.stringify(seid), {1066            httpOnly: false,1067            secure: false,1068            sameSite: 'strict',1069            path: '/',1070            maxAge: undefined1071        });1072        return res.status(200).json({1073            message: 'Profile image updated successfully',1074            image_url: imageUrl1075        });1076    } catch (error) {1077        console.error('Error uploading profile image:', error);1078        res.status(500).json({ message: 'Server error' });1079    }1080};1081 1082 1083exports.streamImage = async (req, res) => {1084    try {1085        const { filename } = req.params;1086 1087        if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {1088            return res.status(400).json({ message: 'Invalid filename' });1089        }1090 1091        let imagePath;1092 1093        const profilePath = path.join(UPLOADS_DIR, filename);1094        if (fs.existsSync(profilePath)) {1095            imagePath = profilePath;1096        } else {1097 1098            return res.status(404).json({ message: 'Image not found' });1099        }1100 1101        const stats = fs.statSync(imagePath);1102        const fileSize = stats.size;1103        const ext = path.extname(filename).toLowerCase();1104 1105        const mimeTypes = {1106            '.jpg': 'image/jpeg',1107            '.jpeg': 'image/jpeg',1108            '.png': 'image/png',1109            '.gif': 'image/gif',1110            '.webp': 'image/webp',1111            '.svg': 'image/svg+xml'1112        };1113 1114        const contentType = mimeTypes[ext] || 'application/octet-stream';1115 1116        res.setHeader('Content-Type', contentType);1117        res.setHeader('Content-Length', fileSize);1118        res.setHeader('Cache-Control', 'public, max-age=86400');1119 1120        const stream = fs.createReadStream(imagePath);1121        stream.pipe(res);1122 1123        // Handle stream errors1124        stream.on('error', (err) => {1125            console.error('Error streaming image:', err);1126            if (!res.headersSent) {1127                res.status(500).json({ message: 'Error streaming image' });1128            }1129        });1130 1131    } catch (error) {1132        console.error('Error in streamImage:', error);1133        if (!res.headersSent) {1134            res.status(500).json({ message: 'Server error' });1135        }1136    }1137}1138 1139exports.searchStudents = async (req, res) => {1140    try {1141        const { search, course_id } = req.body;1142        console.log(req.body)1143 1144        if (!search || search.trim().length === 0) {1145            return res.status(200).json({ message: [] });1146        }1147 1148        const searchTerm = `%${search}%`;1149 1150        let query = `1151            SELECT 1152                u.id as student_id,1153                u.email,1154                u.first_name,1155                u.last_name,1156                u.role1157            FROM users u1158            WHERE u.role = 'student'1159            AND (1160                u.id::TEXT ILIKE $1 OR 1161                u.email ILIKE $1 OR1162                CONCAT(u.first_name, ' ', u.last_name) ILIKE $11163            )1164            AND u.id NOT IN (1165                SELECT student_id 1166                FROM student_courses 1167                WHERE course_id = $21168            )1169            ORDER BY u.id1170            LIMIT 101171        `;1172 1173        const result = await pool.query(query, [searchTerm, course_id || 0]);1174 1175        res.status(200).json({ message: result.rows });1176    } catch (error) {1177        console.error('Error searching students:', error);1178        res.status(500).json({ message: 'Server error' });1179    }1180};1181 1182 1183 1184exports.requestPasswordReset = async (req, res) => {1185    try {1186        const { email } = req.body;1187 1188        const existing = await pool.query(1189            "SELECT * FROM users WHERE email = $1",1190            [email]1191        );1192 1193        if (existing.rows.length === 0) {1194            return res.status(200).json({1195                message: 'If an account exists with this email, a reset code has been sent.'1196            });1197        }1198 1199        const resetCode = Math.floor(100000 + Math.random() * 900000);1200 

Showing the first 1,200 of 1324 lines. Download the file for the rest.