diff --git a/backend/index.js b/backend/index.js
index f52fe56..4a961c9 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -1,3 +1,4 @@
+require("dotenv").config();
const https = require('https');
const fs = require('fs');
const express = require('express');
@@ -11,15 +12,18 @@
const aboutRoutes = require('./routes/about');
// Set up SSL certificate files
-// Define the paths to your certificate and key files
const options = {
- key: fs.readFileSync('/app/certificates/ssl.key'),
- cert: fs.readFileSync('/app/certificates/ssl.crt'),
+ key: fs.readFileSync(process.env.SSL_KEY_PATH),
+ cert: fs.readFileSync(process.env.SSL_CERT_PATH),
};
+const ORIGIN = process.env.ORIGIN ? process.env.ORIGIN.split(",") : [];
+
+console.log("ORIGIN:", ORIGIN);
+
// CORS configuration
const corsOptions = {
- origin: ['https://api.nunoteixeira.dev', 'https://nunoteixeira.dev', 'http://localhost'], // Allow both production and local frontend
+ origin: process.env.ORIGIN ? process.env.ORIGIN.split(",") : [],
methods: 'GET,POST,PUT,DELETE',
credentials: true,
};
@@ -45,6 +49,6 @@
app.use('/', aboutRoutes);
// Start the HTTPS server
-https.createServer(options, app).listen(3001, () => {
- console.log('Server is running on https://www.jmpteixeira.myasustor.com:3001');
+https.createServer(options, app).listen(process.env.PORT, () => {
+ console.log(`Server is running on: ${process.env.BASE_URL}:${process.env.PORT}`);
});
\ No newline at end of file
diff --git a/backend/routes/projects.js b/backend/routes/projects.js
index 9a7c3e6..737d68a 100644
--- a/backend/routes/projects.js
+++ b/backend/routes/projects.js
@@ -13,66 +13,82 @@
const appendBaseUrl = (path) => (path ? `${BASE_URL}${path}` : null);
// Fetch all projects
-router.get("/project_cards", async (req, res) => {
+router.get("/projects", async (req, res) => {
try {
- const query = `
- SELECT
- p.id,
- p.title,
- p.hook,
- p.category,
- p.tags,
+ 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;`;
- (SELECT JSON_ARRAYAGG(t1.name)
+ const projectsQuery = ` SELECT
+ p.id,
+ p.title,
+ p.hook,
+ c.name AS category,
+ p.thumbnail_path AS thumbnail,
- FROM project_tools pt
+ -- 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,
- INNER JOIN tools t1 ON pt.tool_id = t1.id
+ -- 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,
- 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,
- (SELECT JSON_ARRAYAGG(t2.name)
+ -- 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,
- FROM project_tech pt2
+ -- 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,
- INNER JOIN tech t2 ON pt2.tech_id = t2.id
+ -- 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
- WHERE pt2.project_id = p.id) AS tech,
-
- (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,
-
- (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,
-
- (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,
-
- p.thumbnail_path AS thumbnail
- FROM
- projects p
- GROUP BY
- p.id, p.title, p.hook, p.category, p.tags, p.thumbnail_path;
- `;
+ 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;`;
- const rows = await pool.query(query);
+ //Queries results
+ const projectsRaw = await pool.query(projectsQuery);
+ const categoriesAndTagsRaw = await pool.query(categoriesAndTagsQuery);
- const projectsWithImageURLs = rows.map(project => ({
+ 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: [
@@ -85,11 +101,14 @@
//console.log("Query result with image URLs:", projectsWithImageURLs);
// Check if rows are returned
- if (!rows || rows.length === 0) {
+ if (!projectsRaw || projectsRaw.length === 0) {
return res.status(404).send("No projects found.");
}
- res.json(projectsWithImageURLs); // Send the result with image URLs
+ //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");
diff --git a/frontend/src/assets/index.js b/frontend/src/assets/index.js
index 7a6d908..050a8e8 100644
--- a/frontend/src/assets/index.js
+++ b/frontend/src/assets/index.js
@@ -5,25 +5,15 @@
import pdf from "./personal/pdf.png";
//icons
-import hamburger from "./icons/hamburger.svg";
-import alltags from "./icons/alltags.webp";
+import all_tags from "./icons/alltags.webp";
import tag_icon from "./icons/tag_icon.svg";
-import games from "./icons/games.webp";
-import game_systems from "./icons/game_systems.webp";
-import websites from "./icons/websites.webp";
-import apps from "./icons/apps.webp";
import peek_icon from "./icons/peek_icon.svg";
export {
//icons
- hamburger,
peek_icon,
- games,
- alltags,
+ all_tags,
tag_icon,
- game_systems,
- websites,
- apps,
pdf,
//picture
diff --git a/frontend/src/components/ProjectCard.jsx b/frontend/src/components/ProjectCard.jsx
index 37eb89d..b148ee0 100644
--- a/frontend/src/components/ProjectCard.jsx
+++ b/frontend/src/components/ProjectCard.jsx
@@ -29,7 +29,7 @@
onMouseEnter={() => setHoveredProjectId(project.id)}
onMouseLeave={() => setHoveredProjectId(null)}
>
- {/* Only the image is inside the link */}
+ {/* Project Image with the link*/}
+ @2025 Nuno Teixeira
+ All rights reserved
+
diff --git a/frontend/src/components/sections/MainPage.jsx b/frontend/src/components/sections/MainPage.jsx
index df8191d..0f680d4 100644
--- a/frontend/src/components/sections/MainPage.jsx
+++ b/frontend/src/components/sections/MainPage.jsx
@@ -23,7 +23,7 @@
// State to store fetched data
const [introData, setIntroData] = useState([]);
const [aboutMe, setAboutData] = useState([]);
- const [projects, setWorkData] = useState([]);
+ const [workData, setWorkData] = useState([]);
const [skillsData, setSkillsData] = useState([]);
const [activityData, setActivityData] = useState([]);
const [contactData, setContactData] = useState([]);
@@ -111,7 +111,7 @@
{/* Work Section */}