Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
MAIL_API_KEY: ${{ secrets.MAIL_API_KEY }}
RAPID_API_KEY: ${{ secrets.RAPID_API_KEY }}
TUMBLR_CONSUMER_KEY: ${{ secrets.TUMBLR_CONSUMER_KEY }}

jobs:
build-and-ship:
Expand Down Expand Up @@ -67,3 +67,4 @@ jobs:
flags: '--service-min-instances=3'
env_vars: |
MAIL_API_KEY=${{ env.MAIL_API_KEY }}
TUMBLR_CONSUMER_KEY=${{ env.TUMBLR_CONSUMER_KEY }}
2 changes: 2 additions & 0 deletions api-server/routes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ const aboutRouter = require('./about');
const cvRouter = require('./cv');
const worksRouter = require('./works');
const contactRouter = require('./contact');
const postsRouter = require('./logues');

const URL_PREFIX = '/api/v1';

router.use(URL_PREFIX + '/about', aboutRouter);
router.use(URL_PREFIX + '/cv', cvRouter);
router.use(URL_PREFIX + '/works', worksRouter);
router.use(URL_PREFIX + '/contact', contactRouter);
router.use(URL_PREFIX + '/posts', postsRouter);

module.exports = router;
38 changes: 38 additions & 0 deletions api-server/routes/logues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const express = require('express');
const router = express.Router();
const axios = require('axios');

router.get('/', async (req, res, next) => {
// #swagger.tags = ['Logues']
// #swagger.summary = 'returns dynamic content for the blog page'
// #swagger.description = '/api/v1/logues'

const CONSUMER_KEY = process.env.TUMBLR_CONSUMER_KEY;
const url = 'https://api.tumblr.com/v2/blog/hwakabh.tumblr.com/posts?api_key=' + CONSUMER_KEY;
const respPosts = await axios.get(url)
.then(response => {
return response.data.response.posts
})
.catch(error => {
console.log(error);
})
Comment on lines +10 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and API request structure.

Several issues with the current implementation:

  1. Missing error handling: If the Tumblr API fails, respPosts will be undefined, causing a crash on line 21.
  2. API key security: Concatenating the API key in the URL string is less secure than using query parameters.
  3. No error response: Errors are only logged but not returned to the client.

Apply this diff to improve error handling and security:

  const CONSUMER_KEY = process.env.TUMBLR_CONSUMER_KEY;
-  const url = 'https://api.tumblr.com/v2/blog/hwakabh.tumblr.com/posts?api_key=' + CONSUMER_KEY;
-  const respPosts = await axios.get(url)
-    .then(response => {
-      return response.data.response.posts
-    })
-    .catch(error => {
-      console.log(error);
-    })
+  
+  if (!CONSUMER_KEY) {
+    return res.status(500).json({ error: 'Tumblr API key not configured' });
+  }
+
+  try {
+    const response = await axios.get('https://api.tumblr.com/v2/blog/hwakabh.tumblr.com/posts', {
+      params: { api_key: CONSUMER_KEY }
+    });
+    
+    const respPosts = response.data.response.posts || [];

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In api-server/routes/logues.js around lines 10 to 18, improve error handling by
using try-catch instead of mixing await with then/catch, so you can handle
errors properly and avoid respPosts being undefined. Use axios with a params
object to pass the API key securely instead of concatenating it in the URL
string. In the catch block, log the error and send an appropriate error response
to the client to avoid silent failures.


const posts = [];
respPosts.forEach(p => {
posts.push({
date: p.date,
body: p.body
})
});

console.log(posts);
Comment on lines +20 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove debug logging and add validation.

The current implementation has production code issues:

  1. Debug logging: console.log(posts) should be removed from production code.
  2. Missing validation: No check if respPosts exists before processing.

Apply this diff:

  const posts = [];
-  respPosts.forEach(p => {
-    posts.push({
-      date: p.date,
-      body: p.body
-    })
-  });
-
-  console.log(posts);
+  if (respPosts && Array.isArray(respPosts)) {
+    respPosts.forEach(p => {
+      posts.push({
+        date: p.date,
+        body: p.body
+      })
+    });
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const posts = [];
respPosts.forEach(p => {
posts.push({
date: p.date,
body: p.body
})
});
console.log(posts);
const posts = [];
if (respPosts && Array.isArray(respPosts)) {
respPosts.forEach(p => {
posts.push({
date: p.date,
body: p.body
})
});
}
🤖 Prompt for AI Agents
In api-server/routes/logues.js between lines 20 and 28, remove the debug
statement console.log(posts) to clean up production code and add a validation
check to ensure respPosts exists and is an array before iterating over it. This
prevents runtime errors if respPosts is undefined or null. Use a conditional
statement to verify respPosts is valid before the forEach loop.


res.header('Content-Type', 'application/json; charset=utf-8');
res.json({
"path": req.originalUrl,
"content": posts
});

Comment on lines +12 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add proper error handling for the entire route.

The route needs comprehensive error handling to prevent crashes and provide meaningful responses to clients.

Wrap the entire route logic in a try-catch block:

router.get('/', async (req, res, next) => {
  // #swagger.tags = ['Logues']
  // #swagger.summary = 'returns dynamic content for the blog page'
  // #swagger.description = '/api/v1/logues'

+  try {
    const CONSUMER_KEY = process.env.TUMBLR_CONSUMER_KEY;
    
    if (!CONSUMER_KEY) {
      return res.status(500).json({ error: 'Tumblr API key not configured' });
    }

    const response = await axios.get('https://api.tumblr.com/v2/blog/hwakabh.tumblr.com/posts', {
      params: { api_key: CONSUMER_KEY }
    });
    
    const respPosts = response.data.response.posts || [];
    const posts = [];
    
    if (respPosts && Array.isArray(respPosts)) {
      respPosts.forEach(p => {
        posts.push({
          date: p.date,
          body: p.body
        })
      });
    }

    res.header('Content-Type', 'application/json; charset=utf-8');
    res.json({
      "path": req.originalUrl,
      "content": posts
    });

+  } catch (error) {
+    console.error('Error fetching Tumblr posts:', error);
+    res.status(500).json({ 
+      error: 'Failed to fetch blog posts',
+      path: req.originalUrl 
+    });
+  }
});

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In api-server/routes/logues.js between lines 12 and 35, the route lacks
comprehensive error handling which can cause crashes and unclear client
responses. Wrap the entire route logic inside a try-catch block to catch any
errors during execution. In the catch block, log the error and send a meaningful
error response to the client with an appropriate HTTP status code, such as 500,
to indicate a server error.

});

module.exports = router;
14 changes: 14 additions & 0 deletions schemas/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,20 @@
}
}
}
},
"/api/v1/posts/": {
"get": {
"tags": [
"Logues"
],
"summary": "returns dynamic content for the blog page",
"description": "/api/v1/logues",
"responses": {
"200": {
"description": "OK"
}
}
}
}
}
}
83 changes: 23 additions & 60 deletions web-frontend/src/pages/Blog.vue
Original file line number Diff line number Diff line change
@@ -1,67 +1,30 @@
<template>
<section class="wrapper fullscreen">
<div class="inner">
<h1 class="major">Elements</h1>
<section>
<h2>Text</h2>
<p>This is <b>bold</b> and this is <strong>strong</strong>. This is <i>italic</i> and this is <em>emphasized</em>.
This is <sup>superscript</sup> text and this is <sub>subscript</sub> text.
This is <u>underlined</u> and this is code: <code>for (;;) { ... }</code>. Finally, <a href="#">this is a link</a>.</p>
<hr />
<p>Nunc lacinia ante nunc ac lobortis. Interdum adipiscing gravida odio porttitor sem non mi integer non faucibus ornare mi ut ante amet placerat aliquet. Volutpat eu sed ante lacinia sapien lorem accumsan varius montes viverra nibh in adipiscing blandit tempus accumsan.</p>
<hr />
<h2>Heading Level 2</h2>
<h3>Heading Level 3</h3>
<h4>Heading Level 4</h4>
<hr />
<h3>Blockquote</h3>
<blockquote>Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan faucibus. Vestibulum ante ipsum primis in faucibus lorem ipsum dolor sit amet nullam adipiscing eu felis.</blockquote>
<h3>Preformatted</h3>
<pre><code>i = 0;
<script setup>
import axios from "axios";
import { ref } from "vue";

while (!deck.isInOrder()) {
print 'Iteration ' + i;
deck.shuffle();
i++;
}
const postData = ref([]);
axios.get('/api/v1/posts')
.then((resp) => {
postData.value = resp.data.content;
})
.catch((err) => {
console.log(err);
})

print 'It took ' + i + ' iterations to sort the deck.';</code></pre>
</section>
</script>

<!-- Lists -->
<section>
<h2>Lists</h2>
<div class="row">
<div class="col-4 col-12-medium">
<h3>Unordered</h3>
<ul>
<li>Dolor pulvinar etiam.</li>
<li>Sagittis adipiscing.</li>
<li>Felis enim feugiat.</li>
</ul>
</div>
<div class="col-4 col-12-medium">
<h3>Alternate</h3>
<ul class="alt">
<li>Dolor pulvinar etiam.</li>
<li>Sagittis adipiscing.</li>
<li>Felis enim feugiat.</li>
</ul>
</div>
<div class="col-4 col-12-medium">
<h3>Ordered</h3>
<ol>
<li>Dolor pulvinar etiam.</li>
<li>Etiam vel felis viverra.</li>
<li>Felis enim feugiat.</li>
<li>Dolor pulvinar etiam.</li>
<li>Etiam vel felis lorem.</li>
<li>Felis enim et feugiat.</li>
</ol>
</div>
<template>
<section class="wrapper fullscreen">
<div class="inner">
<h1 class="major">Blogs</h1>
<div v-for="(p, idx) in postData"
:key="idx"
class="post-detail"
>
<div v-html="p.date"></div>
<div v-html="p.body"></div>
<hr />
</div>
</section>

</div>
</section>
</template>