Newer
Older
My-Portfolio / backend / routes / projects.js
const express = require('express');
const router = express.Router();
const pool = require("../db");
const fetch = require('node-fetch');
require("dotenv").config();


const BASE_URL = process.env.BASE_URL;
const GIT_API_URL = process.env.GIT_API_URL;
const REPOSITORY_BASE_URL = process.env.REPOSITORY_BASE_URL;

// Helper function to append BASE_URL to a path
const appendBaseUrl = (path) => (path ? `${BASE_URL}${path}` : null);

router.get("/my_projects", async (req, res) => {    
    try {

        //Query
        const projectsQuery = `SELECT * FROM projects;`;
        
        //Queries results
        const projects= await pool.query(projectsQuery);

        //Send response
        res.json(projects);

    } catch (err) {
    console.error("Error fetching project names: ", err.message);
    res.status(500).send("Server error");
    }
});

// Fetch all projects
router.get("/projects", async (req, res) => {
    try {
        const categoriesAndTagsQuery = `SELECT 
                                            c.id AS category_id, 
                                            c.name AS category_name, 
                                            c.logo AS category_logo,
                                            GROUP_CONCAT(t.name) AS tags
                                        FROM 
                                            categories c
                                        LEFT JOIN 
                                            tags t ON c.id = t.category_id
                                        GROUP BY 
                                            c.id;`;

        const projectsQuery = ` SELECT 
                                    p.id, 
                                    p.title, 
                                    p.hook,
                                    c.name AS category,
                                    p.thumbnail_path AS thumbnail,

                                    -- Fetch tags as a JSON array
                                    (SELECT JSON_ARRAYAGG(t.name) 
                                    FROM project_tags pt
                                    INNER JOIN tags t ON pt.tag_id = t.id 
                                    WHERE pt.project_id = p.id) AS tags,

                                    -- Fetch tools as a JSON array
                                    (SELECT JSON_ARRAYAGG(t1.name) 
                                    FROM project_tools pt
                                    INNER JOIN tools t1 ON pt.tool_id = t1.id 
                                    WHERE pt.project_id = p.id) AS tools,

                                    -- Fetch tech as a JSON array
                                    (SELECT JSON_ARRAYAGG(t2.name) 
                                    FROM project_tech pt2 
                                    INNER JOIN tech t2 ON pt2.tech_id = t2.id 
                                    WHERE pt2.project_id = p.id) AS tech,

                                    -- Fetch programming languages as a JSON array
                                    (SELECT JSON_ARRAYAGG(t3.name) 
                                    FROM project_languages pt3 
                                    INNER JOIN languages t3 ON pt3.language_id = t3.id
                                    WHERE pt3.project_id = p.id) AS programming_languages,

                                    -- Fetch tech logos as a JSON array
                                    (SELECT JSON_ARRAYAGG(t2.logo) 
                                    FROM project_tech pt2 
                                    INNER JOIN tech t2 ON pt2.tech_id = t2.id 
                                    WHERE pt2.project_id = p.id) AS tech_logo,

                                    -- Fetch programming language logos as a JSON array
                                    (SELECT JSON_ARRAYAGG(t3.logo) 
                                    FROM project_languages pt3
                                    INNER JOIN languages t3 ON pt3.language_id = t3.id 
                                    WHERE pt3.project_id = p.id) AS programming_languages_logo

                                FROM 
                                    projects p
                                LEFT JOIN 
                                    categories c ON p.category_id = c.id
                                GROUP BY 
                                    p.id, p.title, p.hook, p.thumbnail_path, c.name;`;
        
        //Queries results
        const projectsRaw = await pool.query(projectsQuery);
        const categoriesAndTagsRaw = await pool.query(categoriesAndTagsQuery);

        const categories = categoriesAndTagsRaw.map(category => ({
                ...category,
                category_logo: category.category_logo ? `${BASE_URL}${category.category_logo}` : null,
                tags: category.tags ? category.tags.split(',') : []
            }
        ));

        const projects = projectsRaw.map(project => ({
            ...project,
            image_url: `${BASE_URL}${project.thumbnail}`, // Construct full URL for thumbnail
            logos: [
                ...(project.tech_logo ? project.tech_logo.map(logo => `${BASE_URL}${logo}`) : []),
                ...(project.programming_languages_logo ? project.programming_languages_logo.map(logo => `${BASE_URL}${logo}`) : [])
            ] // Merge tech_logo and programming_languages_logo into a single array
        }));

        // Log the query results
        //console.log("Query result with image URLs:", projectsWithImageURLs);

        // Check if rows are returned
        if (!projectsRaw || projectsRaw.length === 0) {
            return res.status(404).send("No projects found.");
        }

        //console.log("Work result: ", {categories, projects});

        res.json({categories, projects});

    } catch (err) {
        console.error("Error fetching projects:", err.message);
        res.status(500).send("Server error");
    }
});


router.get("/project_page/:project_id", async (req, res) => {
    const { project_id } = req.params;

    try {
        // Query to fetch project context
        const contextQuery = `
            SELECT type AS project_context
            FROM projects p
            WHERE p.id = ?;
        `;

        const contextResult = await pool.query(contextQuery, [project_id]);

        if (!contextResult || contextResult.length === 0) {
            return res.status(404).send("Project not found");
        }

        const projectContext = contextResult[0].project_context;

        // Determine the logo type based on the project context
        let contextLogoType;
        switch (projectContext) {
            case "Personal":
                contextLogoType = "Personal";
                break;
            case "Academic":
                contextLogoType = "Academic";
                break;
            default:
                contextLogoType = "Professional";
        }

        // Query to fetch the logo path
        const logoQuery = `
            SELECT l.logo_path AS context_logo
            FROM logos l
            WHERE l.type = ?;
        `;

        const logoResult = await pool.query(logoQuery, [contextLogoType]);

        if (!logoResult || logoResult.length === 0) {
            return res.status(404).send("Logo not found");
        }

        const logoPath = logoResult[0].context_logo;

        // Construct the project context object
        const projectContextData = {
            project_context: projectContext,
            context_logo: appendBaseUrl(logoPath),
        };

        // Query to fetch Git repository
        const projectQuery = `
            SELECT git_rep AS git_repository
            FROM projects
            WHERE id = ?;
        `;

        const projectDateQuery = `
            SELECT 
                CONCAT(MONTHNAME(p.start_date), ' ', DAY(p.start_date), ', ', YEAR(p.start_date)) AS start_full_date,
                CONCAT(MONTHNAME(p.end_date), ' ', DAY(p.end_date), ', ', YEAR(p.end_date)) AS end_full_date,
                l.logo_path AS date_logo
            FROM projects p
            LEFT JOIN logos l ON l.type = 'date'
            WHERE p.id = ?;
        `;

        const [projectResult, projectExtraInfoResult] = await Promise.all([
            pool.query(projectQuery, [project_id]),
            pool.query(projectDateQuery, [project_id]),
        ]);

        if (!projectResult || projectResult.length === 0) {
            return res.status(404).send("Project not found.");
        }

        const gitRepository = projectResult[0].git_repository;

        let repository = {
            url: null,
            logo: null,
            commits: null,
        };

        if (gitRepository) {
            const repositoryapiUrl = `${GIT_API_URL}${gitRepository}/commits?per_page=5`;
            const repositoryUrl = `${REPOSITORY_BASE_URL}${gitRepository}`;

            const response = await fetch(repositoryapiUrl);
            const commits = await response.json();

            const logoQueryGit = "SELECT logo FROM tools WHERE name = 'GitBucket'";
            const logoResultGit = await pool.query(logoQueryGit);
            const logoUrl = logoResultGit[0]?.logo || '';
            const fullLogoUrl = appendBaseUrl(logoUrl);

            const commitsData = commits.map(commit => ({
                sha: commit.sha,
                author: commit.commit.author.name,
                message: commit.commit.message,
                date: commit.commit.author.date,
                url: commit.html_url,
                logo: fullLogoUrl,
            }));

            repository = {
                url: repositoryUrl,
                logo: fullLogoUrl,
                commits: commitsData,
            };
        }

        // Main query to fetch project page details
        const query = `
        SELECT
            p.id AS project_id,
            p.title AS project_title,
            p.description AS project_description,
            p.hook,
            p.trailer,
            p.thumbnail_path AS project_thumbnail,
            p.screenshots,
    
            -- Fetch tags as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', t.name, 'logo', t.logo)) 
             FROM project_tags pt
             INNER JOIN tags t ON pt.tag_id = t.id 
             WHERE pt.project_id = p.id) AS tags,    
    
            -- Fetch tech as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', t.name, 'logo', t.logo))
             FROM project_tech pt
             INNER JOIN tech t ON pt.tech_id = t.id
             WHERE pt.project_id = p.id) AS tech,
    
            -- Fetch programming languages as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', t.name, 'logo', t.logo))
             FROM project_languages pl
             INNER JOIN languages t ON pl.language_id = t.id
             WHERE pl.project_id = p.id) AS programming_languages,

            -- Fetch development tools as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', dt.name, 'logo', dt.logo)) 
             FROM project_dev_tools pdt
             INNER JOIN dev_tools dt ON pdt.dev_tool_id = dt.id 
             WHERE pdt.project_id = p.id) AS dev_tools,

            -- Fetch management tools as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', mt.name, 'logo', mt.logo)) 
             FROM project_manage_tools pmt
             INNER JOIN manage_tools mt ON pmt.manage_tool_id = mt.id 
             WHERE pmt.project_id = p.id) AS management_tools,

            -- Fetch design tools as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', dt.name, 'logo', dt.logo)) 
             FROM project_design_tools pdt
             INNER JOIN design_tools dt ON pdt.design_tool_id = dt.id 
             WHERE pdt.project_id = p.id) AS des_tools,
    
            -- Fetch databases as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', d.name, 'logo', d.logo))
             FROM project_databases pd
             INNER JOIN \`databases\` d ON pd.database_id = d.id
             WHERE pd.project_id = p.id) AS project_databases,  -- Changed alias to avoid reserved keyword
    
            -- Fetch category as a JSON object
            JSON_OBJECT('name', c.name, 'logo', c.logo) AS category,
    
            -- Fetch project links as a JSON array
            (SELECT JSON_ARRAYAGG(JSON_OBJECT('name', l.name, 'url', l.url, 'logo', l.logo))
            FROM links l
            WHERE l.project_id = p.id) AS links,
    
            pg.background_image AS background_path,
            pg.project_logo AS logo
        FROM 
            project_page pp
        INNER JOIN 
            projects p ON pp.project_id = p.id
        INNER JOIN 
            page pg ON pp.page_id = pg.id
        LEFT JOIN 
            categories c ON p.category_id = c.id
        WHERE 
            pp.project_id = ?;
    `;

        const rows = await pool.query(query, [project_id]);

        const project_skills = rows.map(row => {
            let skills = {}; // Initialize as empty object
        
            if (row.tech && row.tech.length > 0) {
                skills.tech = row.tech.map(t => ({
                    skill_name: "Tech",
                    ...t,
                    logo: appendBaseUrl(t.logo),
                }));
            }
        
            if (row.programming_languages && row.programming_languages.length > 0) {
                skills.programming_languages = row.programming_languages.map(l => ({
                    skill_name: "Programming Languages",
                    ...l,
                    logo: appendBaseUrl(l.logo),
                }));
            }
        
            if (row.development_tools && row.development_tools.length > 0) {                
                skills.development_tools = row.development_tools.map(dev => ({
                    skill_name: "Development Tools",
                    ...dev,
                    logo: appendBaseUrl(dev.logo),
                }));
            }
        
            if (row.management_tools && row.management_tools.length > 0) {                
                skills.management_tools = row.management_tools.map(mgt => ({
                    skill_name: "Management Tools",
                    ...mgt,
                    logo: appendBaseUrl(mgt.logo),
                }));
            }
        
            if (row.des_tools && row.des_tools.length > 0) {                
                skills.design_tools = row.des_tools.map(dt => ({
                    skill_name: "Design Tools",
                    ...dt,
                    logo: appendBaseUrl(dt.logo),
                }));
            }
        
            if (row.project_databases && row.project_databases.length > 0) {                
                skills.project_databases = row.project_databases.map(db => ({
                    skill_name: "Databases",
                    ...db,
                    logo: appendBaseUrl(db.logo),
                }));
            }
        
            return skills; // Return object with all accumulated skills
        });
        

        if (!rows || rows.length === 0) {
            return res.status(404).send("No page found for the given project ID.");
        }

        // Construct the final response
        const pageData = rows.map(row => ({
            ...row,

            category: row.category ? {
                ...row.category,
                logo: appendBaseUrl(row.category.logo),
            } : null,

            skills: project_skills,

            links: row.links ? row.links.map(link => ({
                ...link,
                logo: appendBaseUrl(link.logo),
            })) : null,

            tags: (row.tags || []).map(item => ({
                ...item,
                logo: appendBaseUrl(item.logo),
            })),
            background_image: appendBaseUrl(row.background_path),
            project_logo: appendBaseUrl(row.logo),
            project_thumbnail: appendBaseUrl(row.project_thumbnail),
            repository,
            context: projectContextData, // Include the project context data
            
            // Update date extraction using the new format
            start_date: projectExtraInfoResult[0]?.start_full_date, 
            end_date: projectExtraInfoResult[0]?.end_full_date || "Present", // Ensure "Present" if null
            date_logo: appendBaseUrl(projectExtraInfoResult[0]?.date_logo),

            // Split and concatenate screenshots with the base URL
            screenshots: row.screenshots 
            ? row.screenshots.split(",").map(path => `${BASE_URL}${path.trim()}`) 
            : [],
        }));

        //console.log(pageData);

        res.json(pageData[0]);
    } catch (err) {
        console.error("Error fetching project page:", err.message);
        res.status(500).send("Server error");
    }
});

// Export the router
module.exports = router;