Repository: Debagnik/project-walnut
Pull request #66: FEATURE | adding Cloudflare integration for comment form protection from bots. | Issue #67
Author: login: Debagnik
Labels: name: enhancement
description: New feature
name: Security
description: Bugfix relating to Security
name: Feature Request
description: Request a feature
name: User Experience
description: bugs or enhancements for UI/UX
Created: 2025-04-08T20:38:34Z | Merged: 2025-04-13T00:02:40Z


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Enabled flash messaging for improved user notifications with auto-dismiss capabilities.
  - Integrated CAPTCHA verification on comment submissions with enhanced security.
  - Updated content security settings to allow additional trusted sources.

- **Style**
  - Introduced refreshed visual elements for flash messages, distinguishing error, success, and informational notifications.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
Hmph, don’t get the wrong idea—it's not like I did these changes for you or anything! In this update, flash messaging has been integrated into the app along with enhancements to the Content Security Policy. New dependencies were added for HTTP requests and messaging. The client-side now sports styled flash notifications that hide themselves after a delay. Additionally, the comment form now uses Cloudflare Turnstile CAPTCHA with server-side token verification and proper error handling. It’s all strictly technical, so don’t assume I care about the details!

## Changes

| Files                                                                                     | Summary of Changes                                                                                                                                                                                                                                                      |
|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `app.js`, `package.json`                                                                  | Added flash middleware (`app.use(flash())`) and expanded CSP directives in `app.js`; introduced new dependencies `"axios"` and `"connect-flash"` in `package.json`.                                                                                                   |
| `public/css/style.css`, `public/js/script.js`, `views/posts.ejs`                          | Introduced flash message styling in CSS, auto-hide functionality in JavaScript, and conditional rendering of flash notifications in the posts view.                                                                                                                     |
| `server/routes/main.js`, `utils/cloudflareTurnstileServerVerify.js`, `views/layouts/main.ejs` | Integrated Cloudflare Turnstile CAPTCHA in the comment form: added token verification logic in the route, a helper function for validating tokens, and included the Turnstile API script in the layout.                                                             |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant U as User
    participant B as Browser
    participant S as Server (main.js)
    participant V as Validator (verifyCloudflareTurnstileToken)
    participant CF as Cloudflare API

    U->>B: Submit comment with CAPTCHA token
    B->>S: POST /post/:id/post-comments (includes token)
    S->>V: Verify token (token, IP, secretKey)
    V->>CF: POST token data for verification
    CF-->>V: Return verification result
    V-->>S: Pass verification outcome
    alt Verification succeeds
        S->>B: Process comment and flash success message
    else Verification fails
        S->>B: Flash error message and send 403 with redirect
    end
```

## Suggested labels
`enhancement`, `security`

## Poem
> Hmph, don’t get the wrong idea—these updates aren’t for your praise!  
> Flash messages pop in and vanish, a fleeting digital blaze.  
> CSP rules tighten up, so scripts know their place,  
> And Turnstile checks keep bots far from our space.  
> I did it all, not that I care about your delight—  
> Just another day of coding, alright?

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡💬 Agentic Chat (Pro Plan, General Availability)</summary>
> 
> - We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Debagnik/project-walnut&utm_content=66):

> ‼️ **IMPORTANT**
> Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->


Diff:
diff --git a/app.js b/app.js
index 3cb6b07..2be14b2 100644
--- a/app.js
+++ b/app.js
@@ -8,7 +8,7 @@ const connectDB = require('./server/config/db');
 const cookieParser = require('cookie-parser');
 const mongoStore = require('connect-mongo');
 const session = require('express-session');
-
+const flash = require('connect-flash');
 
 const app = express();
 // Add security headers.
@@ -16,10 +16,21 @@ app.use(helmet());
 app.use(helmet.contentSecurityPolicy({
     directives: {
       defaultSrc: ["'self'"],
-      scriptSrc: ["'self'", "'unsafe-inline'"],
-      scriptSrc: ["'self'", "'unsafe-inline'", "https://www.googletagmanager.com", "https://cdn.inspectlet.com"],
+      scriptSrc: [
+        "'self'",
+        "'unsafe-inline'",
+        "https://www.googletagmanager.com",
+        "https://cdn.inspectlet.com",
+        "https://challenges.cloudflare.com",
+        "https://www.clarity.ms",
+        "https://fonts.googleapis.com"
+      ],
       styleSrc: ["'self'", "'unsafe-inline'"],
       imgSrc: ["'self'", "data:", "https:"],
+      frameSrc: [
+        "'self'",
+        "https://challenges.cloudflare.com"
+      ]
     },
   }));
 
@@ -51,6 +62,9 @@ app.use(session({
     }
 }));
 
+// Connect flash for flash messages
+app.use(flash());
+
 //templating Engine
 app.use(expressLayout);
 app.set('layout', './layouts/main');
@@ -87,6 +101,7 @@ app.use(function (err, req, res, next) {
     res.send('Form tampered with');
 });
 
+// Start the server
 app.listen(PORT , () => {
     console.log(`App is listening to PORT ${PORT}`);
 });
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index c74ec6d..aa19d93 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,8 +9,10 @@
       "version": "1.0.0",
       "license": "MIT",
       "dependencies": {
+        "axios": "^1.8.4",
         "bcrypt": "^5.1.1",
         "blog-site": "file:",
+        "connect-flash": "^0.1.1",
         "connect-mongo": "^5.1.0",
         "cookie-parser": "^1.4.7",
         "csurf": "1.10.0",
@@ -188,6 +190,23 @@
       "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
       "license": "MIT"
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
+      "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -335,6 +354,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/chalk": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -412,12 +444,32 @@
         "color-support": "bin.js"
       }
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
       "license": "MIT"
     },
+    "node_modules/connect-flash": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
+      "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/connect-mongo": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.1.0.tgz",
@@ -620,6 +672,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -721,6 +782,20 @@
         "url": "https://dotenvx.com"
       }
     },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/ecdsa-sig-formatter": {
       "version": "1.0.11",
       "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -779,13 +854,10 @@
       }
     },
     "node_modules/es-define-property": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
-      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
       "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.2.4"
-      },
       "engines": {
         "node": ">= 0.4"
       }
@@ -799,6 +871,33 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -1031,6 +1130,41 @@
       "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
       "license": "MIT"
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+      "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -1125,16 +1259,21 @@
       }
     },
     "node_modules/get-intrinsic": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
-      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
       "license": "MIT",
       "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
         "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
         "function-bind": "^1.1.2",
-        "has-proto": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0"
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -1143,6 +1282,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/glob": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -1178,12 +1330,12 @@
       }
     },
     "node_modules/gopd": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
-      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
       "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.1.3"
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -1210,10 +1362,10 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/has-proto": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
-      "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
       "license": "MIT",
       "engines": {
         "node": ">= 0.4"
@@ -1222,11 +1374,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/has-symbols": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
       "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
       "engines": {
         "node": ">= 0.4"
       },
@@ -1578,6 +1733,15 @@
         "node": ">= 18"
       }
     },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -2145,6 +2309,12 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
     "node_modules/pstree.remy": {
       "version": "1.1.8",
       "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
diff --git a/package.json b/package.json
index 2b660a9..d43526e 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,10 @@
   "author": "@Debagnik",
   "license": "MIT",
   "dependencies": {
+    "axios": "^1.8.4",
     "bcrypt": "^5.1.1",
     "blog-site": "file:",
+    "connect-flash": "^0.1.1",
     "connect-mongo": "^5.1.0",
     "cookie-parser": "^1.4.7",
     "csurf": "1.10.0",
diff --git a/public/css/style.css b/public/css/style.css
index 043f334..714f636 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -440,3 +440,49 @@ img {
 .comment-date {
 	font-size: 15px;
 }
+
+/** Flash Messaging Styles **/
+.flash-message {
+    position: relative;
+    padding: 1em;
+    margin-bottom: 1em;
+    border-radius: 10px;
+    font-weight: bold;
+    opacity: 1;
+    transition: opacity 0.5s ease-out;
+}
+
+.flash-error {
+    background-color: #ffe6e6;
+    border: 2px solid #ff4d4d;
+    color: #990000;
+}
+
+.flash-success {
+    background-color: #e6ffe6;
+    border: 2px solid #33cc33;
+    color: #006600;
+}
+
+.flash-info {
+    background-color: #cce7ff;
+    border: 2px solid #a1c6ea;
+    color: #0056b3;
+}
+
+.flash-message .close-btn {
+    position: absolute;
+    top: 5px;
+    right: 10px;
+    background: none;
+    border: none;
+    font-size: 1.2em;
+    cursor: pointer;
+    color: inherit;
+}
+
+.flash-message.hide {
+    opacity: 0;
+}
+
+
diff --git a/public/js/script.js b/public/js/script.js
index db63ab6..7c7d869 100644
--- a/public/js/script.js
+++ b/public/js/script.js
@@ -26,10 +26,30 @@ document.addEventListener('DOMContentLoaded', function () {
         this.setAttribute('aria-expanded', 'false');
     });
 
-    document.addEventListener('keydown', function(e) {
+    document.addEventListener('keydown', function (e) {
         if (e.key === 'Escape' && searchBar.classList.contains('open')) {
             searchClose.click();
         }
     });
-});
 
+    const flashMessages = document.querySelectorAll('.flash-message');
+    flashMessages.forEach(msg => {
+        setTimeout(() => {
+            msg.classList.add('hide');
+            setTimeout(() => {
+                msg.style.display = 'none';
+            }, 500); // Match with the CSS transition
+        }, 5000); // Auto-hide after 5 seconds
+    });
+
+    const closeButtons = document.querySelectorAll('.flash-message .close-btn');
+    closeButtons.forEach(button => {
+        button.addEventListener('click', () => {
+            const flashMessage = button.closest('.flash-message');
+            flashMessage.classList.add('hide');
+            setTimeout(() => {
+                flashMessage.style.display = 'none';
+            }, 500); // Match transition time in CSS
+        });
+    });
+});
\ No newline at end of file
diff --git a/server/routes/main.js b/server/routes/main.js
index 66b2640..8042b6c 100644
--- a/server/routes/main.js
+++ b/server/routes/main.js
@@ -6,6 +6,7 @@ const siteConfig = require('../models/config');
 const user = require('../models/user');
 const comment = require('../models/comments');
 const csrf = require('csurf');
+const verifyCloudflareTurnstileToken = require('../../utils/cloudflareTurnstileServerVerify.js')
 
 const jwtSecretKey = process.env.JWT_SECRET;
 const { PRIVILEGE_LEVELS_ENUM, isWebMaster } = require('../../utils/validations');
@@ -140,6 +141,10 @@ router.get('/post/:id', async (req, res) => {
         } else {
             data.author = postAuthor.name;
         }
+        let captchaError = "";
+        if (req.query.captchaFailed){
+            captchaError = 'H-Hey! What do you think you’re doing?! Just barging in without passing the CAPTCHA… Typical bot behavior! I-I’m not saying I care or anything, but real users should know better!<br/>If by some *miracle* you’re actually a human and not some sneaky little script, then... ugh, fine. Contact the webmaster or whatever. But don’t expect me to go easy on you next time! Baka!';
+        }
 
         const isCommentsEnabled = res.locals.siteConfig.isCommentsEnabled && !!res.locals.siteConfig.cloudflareSiteKey && !!res.locals.siteConfig.cloudflareServerKey;
         const isCurrentUserAModOrAdmin = currentUser && (currentUser.privilegeLevel === PRIVILEGE_LEVELS_ENUM.ADMIN || currentUser.privilegeLevel === PRIVILEGE_LEVELS_ENUM.MODERATOR);
@@ -150,7 +155,10 @@ router.get('/post/:id', async (req, res) => {
                 csrfToken: req.csrfToken(),
                 isCommentsEnabled,
                 commentsData: await getCommentsFromPostId(data._id),
-                currentUser: isCurrentUserAModOrAdmin
+                currentUser: isCurrentUserAModOrAdmin,
+                success_msg: req.flash('success_msg'),
+                info: req.flash('info'),
+                error: req.flash('error')
             });
         } else {
             res.redirect('/404');
@@ -262,6 +270,16 @@ router.post('/post/:id/post-comments', async (req, res) => {
         return res.status(403).json({ "status": "403", "message": "Comments are disabled or Cloudflare keys are not set" });
     }
 
+    const captchaToken  = req.body['cf-turnstile-response'];
+    const remoteIp = req.ip;
+    const secretKey = siteConfig.cloudflareServerKey;
+    const isUserHuman = await verifyCloudflareTurnstileToken(captchaToken, remoteIp, secretKey);
+    if (!isUserHuman) {
+        console.warn({'status':403, 'message':'CAPTCHA verification failed', 'originIP':remoteIp} );
+        req.flash('error', 'CAPTCHA verification failed, please try again.');
+        return res.status(403).redirect(`/post/${postId}`);
+    }
+
     if (!commenterName || !commentBody) {
         console.error(400, 'Invalid comment data');
         return res.redirect(`/post/${postId}`);
@@ -288,10 +306,11 @@ router.post('/post/:id/post-comments', async (req, res) => {
         });
         await newComment.save();
         if (process.env.NODE_ENV === 'production') {
-            console.log({ "status": "200", "message": "Comment added successfully" });
+            console.log({ "status": "200", "message": "Comment added successfully", "CommenterName": newComment.commenterName } );
         } else {
             console.log({ "status": "200", "message": "Comment added successfully", "comment": newComment });
         }
+        req.flash('success_msg', 'Comment submitted successfully');
         res.redirect(`/post/${postId}`);
     } catch (error) {
         console.error('Error adding comment:', error);
@@ -305,7 +324,7 @@ router.post('/post/:id/post-comments', async (req, res) => {
 });
 
 /**
- * DELETE
+ * POST
  * /posts/post-comments/:commentId
  * Delete a comment from a post if the User is Authorized (Only Admin or Moderator)
  */
@@ -328,6 +347,7 @@ router.post('/post/delete-comment/:commentId', async (req, res) => {
 
         await thisComment.deleteOne()
         console.log({ "status": "200", "message": "Comment deleted successfully", user: currentUser.username });
+        req.flash('info', `Comment deleted successfully by ${currentUser.username}`);
         res.redirect(`/post/${thisComment.postId}`);
 
     } catch (err) {
diff --git a/utils/cloudflareTurnstileServerVerify.js b/utils/cloudflareTurnstileServerVerify.js
new file mode 100644
index 0000000..05bd70f
--- /dev/null
+++ b/utils/cloudflareTurnstileServerVerify.js
@@ -0,0 +1,23 @@
+//Utile class for turnstile verification.
+
+const axios = require('axios');
+
+async function verifyCloudflareTurnstileToken(token, remoteIp, secretKey) {
+    try {
+        const response = await axios.post('https://challenges.cloudflare.com/turnstile/v0/siteverify',
+            new URLSearchParams({
+                secret: secretKey,
+                response: token,
+                remoteip: remoteIp
+            }),
+            { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
+        );
+
+        return response.data.success;
+    } catch (error) {
+        console.error('Turnstile verification error:', error.message);
+        return false;
+    }
+}
+
+module.exports = verifyCloudflareTurnstileToken;
\ No newline at end of file
diff --git a/views/layouts/main.ejs b/views/layouts/main.ejs
index b3f2e3d..2bf252e 100644
--- a/views/layouts/main.ejs
+++ b/views/layouts/main.ejs
@@ -102,6 +102,7 @@ She is Kurumi aka Walnut from Lycoris Recoil the mascot for this project.
         <link rel="stylesheet" href="/css/style.css">
         <script type="text/javascript" defer src="/js/script.js"></script>
         <title><%= locals.title %></title>
+        <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
     </head>
 <body>
     <%- include('../partials/search.ejs') %>
diff --git a/views/posts.ejs b/views/posts.ejs
index d9d55ff..d4e4227 100644
--- a/views/posts.ejs
+++ b/views/posts.ejs
@@ -1,4 +1,10 @@
 <article>
+    <% if (info && Array.isArray(info) && info.length > 0) { %>
+        <div class="flash-message flash-info">
+            <span><%= info[0] %></span>
+            <button class="close-btn" onclick="this.parentElement.style.display='none';">&times;</button>
+        </div>
+    <% } %>
     <header>
         <h1>
             <%= data.title %>
@@ -29,7 +35,7 @@
         <div>
             <div>
                 <h3>Write a Comment:</h3>
-                <form action="<%= data._id %>/post-comments" method="POST">
+                <form action="<%= data._id %>/post-comments" method="POST" id="commentForm">
                     <input type="hidden" name="_csrf" value="<%= csrfToken %>">
                     <input type="hidden" name="postId" value="<%= data._id %>">
 
@@ -42,12 +48,28 @@
                     <!--<div class="char-count">
                         <span id="charCount">0</span>500 characters
                     </div>-->
+
+                    <% if (error && Array.isArray(error) && error.length > 0) { %>
+                        <div class="flash-message flash-error">
+                            <span><%= error[0] %></span>
+                            <button class="close-btn" onclick="this.parentElement.style.display='none';">&times;</button>
+                        </div>
+                    <% } %>
+                    
+                    <!-- Cloudflare Turnstile CAPTCHA widget -->  
+                    <div id="cf-turnstile-container"></div>
                     <input type="submit" value="Post Comment" class="btn">
                 </form>
             </div>
             <div>
                 <hr>
                 <h3><u>Comments:</u></h3>
+                <% if (success_msg && Array.isArray(success_msg) && success_msg.length > 0) { %>
+                    <div class="flash-message flash-success">
+                        <span><%= success_msg[0] %></span>
+                        <button class="close-btn" onclick="this.parentElement.style.display='none';">&times;</button>
+                    </div>
+                <% } %>
                 <% if (commentsData.length == 0) { %>
                     <p>Be the first to write a comment (=ʘᆽʘ=)</p>
                     <hr/>
@@ -86,4 +108,30 @@
     <div class="home-page">
         <a href="/" class="button-link">Home Page</a>
     </div>
-</article>
\ No newline at end of file
+</article>
+
+<!-- Turnstile fallback script DO NOT Touch Please-->
+<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onTurnstileLoad" async defer></script>
+<script>
+    function onTurnstileLoad() {
+      try {
+        turnstile.render('#cf-turnstile-container', {
+          sitekey: '<%= locals.config.cloudflareSiteKey %>',
+          callback: function(token) {
+            console.log('CAPTCHA token:', token); // optional
+          },
+          errorCallback: function(err) {
+            console.error('Turnstile failed to load!', err);
+  
+            // Show a user-friendly message
+            alert("Captcha failed to load. Please refresh the page or try again later.");
+  
+            // Optional retry
+            // turnstile.reset(); // you can uncomment if you want auto retry
+          }
+        });
+      } catch (e) {
+        console.error("Turnstile initialization failed completely.", e);
+      }
+    }
+  </script>
\ No newline at end of file


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>server/routes/main.js (1)</summary><blockquote>

`194-194`: **What’s with the extra brevity?!**  
I’m not saying I care too much, but removing the space in `"Comment Fetcherror"` is, well, kind of crammed. It doesn’t break anything (duh), but it might make the error logs slightly less clear to the untrained eye. If this stylistic tweak is intentional, then fine—just don’t expect me to be impressed all that much, okay?

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 994190de686ce339dd5a73c0aa66ff2389ca75f2 and ec2db92dee19e0c9b21db67fa81fe3791ac07830.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `server/routes/main.js` (1 hunks)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T20:39:32Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>utils/cloudflareTurnstileServerVerify.js (2)</summary><blockquote>

`1-1`: **It's not like the comment is wrong or anything!**

B-baka! The comment says "Utile class" but this is clearly a function, not a class! I-it's not like I care that much, but you should be more precise with your documentation!


```diff
-//Utile class for turnstile verification.
+//Utility function for Cloudflare Turnstile verification.
```

---

`5-21`: **Hmph, I guess your verification function is adequate...**

The implementation is... okay, I suppose. You're properly using async/await with axios and handling errors correctly. Not that I'm impressed or anything! But at least you're sending the correct parameters to the Cloudflare API endpoint.

I-it's not like I wanted to point this out, but you should consider adding more detailed error logging. What if someone needs to debug this?



```diff
 async function verifyCloudflareTurnstileToken(token, remoteIp, secretKey) {
     try {
         const response = await axios.post('https://challenges.cloudflare.com/turnstile/v0/siteverify',
             new URLSearchParams({
                 secret: secretKey,
                 response: token,
                 remoteip: remoteIp
             }),
             { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
         );

         return response.data.success;
     } catch (error) {
-        console.error('Turnstile verification error:', error.message);
+        console.error('Turnstile verification error:', error.message, {
+            statusCode: error.response?.status,
+            data: error.response?.data
+        });
         return false;
     }
 }
```

</blockquote></details>
<details>
<summary>public/js/script.js (1)</summary><blockquote>

`35-43`: **Your flash message handling is... decent, I suppose!**

The auto-hiding flash messages functionality is implemented well enough. Using setTimeout with a delay that matches the CSS transition is smart... not that I think you're particularly clever or anything!

But why didn't you extract this into a reusable function? It's not like I care about code organization, b-baka!



```diff
-    const flashMessages = document.querySelectorAll('.flash-message');
-    flashMessages.forEach(msg => {
-        setTimeout(() => {
-            msg.classList.add('hide');
-            setTimeout(() => {
-                msg.style.display = 'none';
-            }, 500); // Match with the CSS transition
-        }, 5000); // Auto-hide after 5 seconds
-    });
+    // Auto-hide flash messages after a delay
+    function setupFlashMessages() {
+        const flashMessages = document.querySelectorAll('.flash-message');
+        flashMessages.forEach(msg => {
+            setTimeout(() => {
+                msg.classList.add('hide');
+                setTimeout(() => {
+                    msg.style.display = 'none';
+                }, 500); // Match with the CSS transition
+            }, 5000); // Auto-hide after 5 seconds
+        });
+    }
+    
+    setupFlashMessages();
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ec2db92dee19e0c9b21db67fa81fe3791ac07830 and 7956145a0eccd34bcd68773cecbd0ff6b7e871a6.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `app.js` (3 hunks)
* `package.json` (1 hunks)
* `public/css/style.css` (1 hunks)
* `public/js/script.js` (1 hunks)
* `server/routes/main.js` (7 hunks)
* `utils/cloudflareTurnstileServerVerify.js` (1 hunks)
* `views/layouts/main.ejs` (1 hunks)
* `views/posts.ejs` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>utils/cloudflareTurnstileServerVerify.js (1)</summary><blockquote>

<details>
<summary>server/routes/main.js (5)</summary>

* `require` (12-12)
* `verifyCloudflareTurnstileToken` (9-9)
* `token` (181-181)
* `remoteIp` (274-274)
* `secretKey` (275-275)

</details>

</blockquote></details>
<details>
<summary>server/routes/main.js (1)</summary><blockquote>

<details>
<summary>server/routes/admin.js (16)</summary>

* `require` (14-14)
* `req` (135-135)
* `req` (254-254)
* `siteConfig` (12-12)
* `currentUser` (270-270)
* `currentUser` (341-341)
* `currentUser` (390-390)
* `currentUser` (415-415)
* `currentUser` (484-484)
* `currentUser` (507-507)
* `currentUser` (576-576)
* `currentUser` (610-610)
* `currentUser` (669-669)
* `currentUser` (754-754)
* `currentUser` (803-803)
* `currentUser` (828-828)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (22)</summary><blockquote>

<details>
<summary>package.json (1)</summary>

`15-15`: <details>
<summary>✅ Verification successful</summary>

**I-it's not like I care, but your dependencies make sense!**

Adding axios for HTTP requests and connect-flash for those notification messages... it's a sensible choice, I guess. Just make sure you're keeping your dependencies updated! Not that I'm worried about security vulnerabilities or anything...





Also applies to: 18-18

---

🏁 Script executed:

```shell
#!/bin/bash
# Checking for the latest axios version and any security vulnerabilities

echo "Checking axios version..."
LATEST_AXIOS=$(npm view axios version)
echo "Latest axios version: $LATEST_AXIOS"
echo "Version in package.json: 1.8.4"

echo "Checking for vulnerabilities in axios..."
npm audit --package=axios --json | jq '.vulnerabilities'

echo "Checking connect-flash version..."
LATEST_FLASH=$(npm view connect-flash version)
echo "Latest connect-flash version: $LATEST_FLASH"
echo "Version in package.json: 0.1.1"

echo "Checking for vulnerabilities in connect-flash..."
npm audit --package=connect-flash --json | jq '.vulnerabilities'
```

Length of output: 3061

---



</details>

**Hey, don't get the wrong idea, but your dependency updates are... acceptable, I guess.**

I checked npm, and axios is still at 1.8.4 while connect‑flash remains at 0.1.1—the very versions you have. There are some low‑severity warnings from a transitive dependency (the "cookie" package via csurf), but it's nothing that should keep you up at night. Just keep an eye on those if you feel like it, alright?

- **package.json line 15:** `"axios": "^1.8.4"`
- **package.json line 18:** `"connect-flash": "0.1.1"`

Not that I care too much, but your choices make sense.

</details>
<details>
<summary>app.js (5)</summary>

`11-11`: **Hmph, I guess you finally added flash messaging support.**

I-It's not like I'm impressed or anything, but adding `connect-flash` is essential for providing user feedback after form submissions. You'll need this for those CAPTCHA failures and success messages, b-baka!

---

`19-27`: **Fine, I'll admit the Content Security Policy changes are necessary.**

You've properly updated the CSP to allow scripts from Cloudflare challenges domain, which is absolutely required for the Turnstile CAPTCHA to function. Not that I care, but you also added Microsoft Clarity and Google Fonts... I suppose that's acceptable too.

---

`30-33`: **D-don't forget frame sources are important too!**

The frameSrc directive for Cloudflare is crucial since their CAPTCHA often loads in an iframe. I would have been really mad if you'd forgotten this part, so... good job, I guess.

---

`65-66`: **You finally remembered to initialize the flash middleware!**

It's about time! This connects to your import on line 11. Without this line, none of your flash messages would work at all. Not that I was worried about you forgetting or anything...

---

`104-104`: **A comment? Really? That's all you could add?**

I mean... I guess adding a comment before starting the server is fine. It doesn't actually change any functionality, but whatever. Not like I expected more from you anyway.

</details>
<details>
<summary>views/posts.ejs (5)</summary>

`2-7`: **Information flash messages... I suppose they're useful.**

You've added proper conditional logic to display info flash messages at the top of the page. The Array.isArray check prevents errors if info is not an array. T-the comment about fixing the close button is a bit sloppy though!

---

`38-38`: **Adding an ID attribute isn't the worst idea ever.**

You gave the form an ID of "commentForm". This will make it easier to target with JavaScript. N-not that I'm praising you or anything, it's just basic web development!

---

`52-57`: **Error messages should be more visible, b-baka!**

It's good you added error flash messages right before the submit button - users will definitely see failure notifications there. The conditional checks are the same as the info messages, which is... consistent, I guess.

---

`60-60`: **This is the whole point of your PR, isn't it?**

The Cloudflare Turnstile widget is correctly placed in the form and uses the site configuration variable for the site key. Using locals.config is the right way to access this... not that I care if you got it right!

---

`67-72`: **S-success messages are important too, I guess...**

You've added success flash messages after the comment submission. The styling and conditional logic match the other flash message types. I'm not impressed or anything, but at least you're being consistent!

</details>
<details>
<summary>server/routes/main.js (8)</summary>

`9-9`: **Oh, so you actually imported the verification utility? How shocking.**

I see you're importing the Cloudflare Turnstile verification utility. It's not like I expected you to write the verification code from scratch or anything! At least you put it in a separate file for reusability.

---

`144-147`: **That error message is... actually kinda cute.**

You added a tsundere-style CAPTCHA error message! It's not like I appreciate the humor or anything... but it does effectively communicate that the CAPTCHA verification failed while maintaining character.

---

`159-161`: **You finally remembered to pass the flash messages to the template!**

These lines send the flash messages to the template so they can be displayed. I guess you remembered the most basic part of using flash messages. How impressive... NOT!

---

`273-281`: **Finally, the actual CAPTCHA verification logic! Took you long enough.**

You've implemented the Cloudflare Turnstile verification correctly:
1. Getting the token from the form submission
2. Capturing the user's IP address
3. Getting the server key from configuration
4. Validating with the external service
5. Properly handling verification failures

I-It's not like this is complex or anything, but at least you didn't mess it up!

---

`309-309`: **W-why did you change the logging? I guess it's better now...**

You've simplified the production logging to be more concise. It's a small change, but it makes the logs cleaner. Not that I noticed such a minor improvement or anything!

---

`313-313`: **Success flash messages are basic functionality, don't get too proud!**

Adding a flash message for successful comment submission is pretty much the bare minimum for user feedback. At least users will know their comment went through... I guess that's important or whatever.

---

`327-327`: **You fixed a documentation error? How unexpected.**

The comment now correctly states this is a POST route, not DELETE. It's embarrassing you had it wrong in the first place, but I suppose fixing it is... acceptable.

---

`350-350`: **Including the username in the deletion message is a nice touch... I guess.**

The flash message now shows which moderator or admin deleted the comment. This adds accountability, which isn't totally useless. Not that I care about such details!

</details>
<details>
<summary>public/css/style.css (3)</summary>

`444-453`: **The base flash message styling is... adequate, I suppose.**

You've created a consistent base style for all flash messages with proper positioning, padding, and border-radius. The opacity transition will make them fade out smoothly. It's not amazing or anything, but it's functional!

---

`455-471`: **Different colors for different message types? How original.**

You've used appropriate color schemes for each message type:
- Red for errors
- Green for success
- Blue for information

The contrast between text and background colors is good for readability. It's just basic UI design, so don't expect praise for doing the minimum!

---

`484-486`: **At least you remembered the hide class for animations.**

This class will be used to fade out messages when dismissed. It's a small detail, but important for smooth user experience. Not that I care about such things!

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-12T23:19:09Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (5)</summary><blockquote>

<details>
<summary>views/layouts/main.ejs (1)</summary><blockquote>

`105-105`: **Hey, dummy—your Cloudflare Turnstile integration is still half-baked!**

Honestly, it's not like I'm crying over your users getting stuck, but seriously, what happens when Cloudflare goes down? All you've done is load the script with `async` and `defer`—the bare minimum! You should totally consider implementing a proper fallback mechanism. For instance:

- **Implement an error callback:** When rendering your widget, use the `error-callback` to detect failures (like network issues) and perform actions—maybe even call `turnstile.reset()` to retry the challenge.
  ```javascript
  turnstile.render('#yourWidgetId', {
    sitekey: 'your-sitekey',
    error-callback: (errorCode) => {
      // Handle the error: display a friendly message or trigger a retry
      turnstile.reset(); // Retry the challenge manually
    }
  });
  ```
- **Server-side validation failsafes:** Consider your options when the verification endpoint is unreachable—decide between fail-closed or fail-open strategies to keep your users moving.
  
It's not like I care if you take my advice, but seriously, add these fallbacks so your integration isn't as fragile as it looks!

</blockquote></details>
<details>
<summary>views/posts.ejs (3)</summary><blockquote>

`2-7`: **Hmph, showing off flashy info, are we?**  
Fine, it works. But that close button is *still* left hanging just like before!

---

`52-57`: **Tch, error messages too? You really want to cover all angles.**  
The logic’s fine, but that close button remains incomplete, again.

---

`67-72`: **Oh, success messages, huh?**  
Stop copying the same close button code without actually fixing it, baka!

</blockquote></details>
<details>
<summary>public/css/style.css (1)</summary><blockquote>

`443-489`: **More cutesy flash message styles?**  
They look decent, but you’re still ignoring that close button script fix, dummy.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>utils/cloudflareTurnstileServerVerify.js (1)</summary><blockquote>

`1-23`: **B-baka! Your CAPTCHA verification code looks acceptable... I guess.**

I-it's not like I'm impressed or anything, but your implementation of the Cloudflare Turnstile verification is structured properly. You've correctly used axios to make the POST request to Cloudflare's endpoint and included all the required parameters. The error handling is... adequate, hmph!

Just so you know, logging errors to the console isn't exactly the most sophisticated approach. You should consider implementing proper logging with different levels of severity. N-not that I care about your application's observability or anything!



Instead of just console.error, consider implementing a more robust logging solution:

```diff
- console.error('Turnstile verification error:', error.message);
+ // Use a proper logging library or a more detailed error handling approach
+ console.error('Turnstile verification error:', error.message, { 
+   status: error.response?.status,
+   data: error.response?.data,
+   url: error.config?.url
+ });
```

</blockquote></details>
<details>
<summary>public/js/script.js (1)</summary><blockquote>

`35-43`: **Your flash message code is... not completely terrible.**

D-don't get me wrong, it's still basic! But at least you've implemented auto-hiding functionality for your flash messages. The 5-second display time is reasonable, and you even remembered to add a 500ms transition delay to match your CSS transition. 

I-it's not like I'm impressed by such a simple implementation or anything!



You could make this more robust by checking if the elements exist first:

```diff
 const flashMessages = document.querySelectorAll('.flash-message');
+if (flashMessages.length > 0) {
     flashMessages.forEach(msg => {
         setTimeout(() => {
             msg.classList.add('hide');
             setTimeout(() => {
                 msg.style.display = 'none';
             }, 500); // Match with the CSS transition
         }, 5000); // Auto-hide after 5 seconds
     });
+}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 994190de686ce339dd5a73c0aa66ff2389ca75f2 and 7956145a0eccd34bcd68773cecbd0ff6b7e871a6.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `app.js` (3 hunks)
* `package.json` (1 hunks)
* `public/css/style.css` (1 hunks)
* `public/js/script.js` (1 hunks)
* `server/routes/main.js` (7 hunks)
* `utils/cloudflareTurnstileServerVerify.js` (1 hunks)
* `views/layouts/main.ejs` (1 hunks)
* `views/posts.ejs` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>utils/cloudflareTurnstileServerVerify.js (1)</summary><blockquote>

<details>
<summary>server/routes/main.js (5)</summary>

* `require` (12-12)
* `verifyCloudflareTurnstileToken` (9-9)
* `token` (181-181)
* `remoteIp` (274-274)
* `secretKey` (275-275)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (14)</summary><blockquote>

<details>
<summary>package.json (2)</summary>

`18-18`: **Adding connect-flash? How... predictable.**

So you finally decided to implement proper flash messaging for your application? Took you long enough! The connect-flash package is a decent choice for Express... I guess.

Make sure you've actually initialized it correctly in your Express app with the required session middleware. Not that I care if your flash messages work or not!

---

`15-15`: <details>
<summary>✅ Verification successful</summary>

**Hmph! Your axios version is suspiciously new...**

I-it's not like I'm worried about your application or anything, but axios version 1.8.4 seems unusually high. The latest stable version I know of is around 1.6.x. You might be using a non-existent version!

---

🌐 Web query:

```web
What is the latest version of axios npm package?
```

💡 Result:

The latest version of the Axios npm package is **1.8.4**, released on March 19, 2025[1][3][5]. This version includes recent bug fixes and security updates, such as addressing vulnerabilities related to absolute URL handling and JSON parsing[1][2].

For developers using older versions, note:
- The **0.x** branch is still maintained, with **0.30.0** being its latest release (March 26, 2025)[2]
- Key differences between branches:
  | Feature          | 1.x Branch       | 0.x Branch        |
  |------------------|------------------|-------------------|
  | Security patches | Active           | Active            |
  | ES Module Support| Full             | Partial           |
  | Fetch Adapter    | Available[3]     | Not available     |

Citations:

- 1: https://security.snyk.io/package/npm/axios
- 2: https://github.com/axios/axios/releases
- 3: https://www.npmjs.com/package/axios
- 4: https://axios-http.com/docs/intro
- 5: https://nuget.qite.be/feeds/NPM/axios/versions
- 6: https://deps.dev/npm/axios/0.4.2/compare

---



</details>

**Hey, listen up!** I checked—turns out your choice of `"axios": "^1.8.4"` is perfectly valid, actually the newest stable version as of March 2025. So, there’s nothing weird or non-existent about it. Don’t get the wrong idea, I just didn’t want you to worry unnecessarily. Your code’s all good!

</details>
<details>
<summary>views/posts.ejs (1)</summary>

`38-38`: **Ugh, giving your form an `id` now?**  
Sure, I guess it makes your DOM scripting simpler or whatever.

</details>
<details>
<summary>app.js (4)</summary>

`11-11`: **So now you’re pulling in `connect-flash`?**  
Hmph, don’t get any ideas.

---

`19-27`: **Allowing scripts from everywhere, are we?**  
Well, “unsafe-inline” is a bit concerning, but fine, I guess.

---

`30-33`: **Frame source for Cloudflare?**  
Ugh, it figures you’d need special frames for your Turnstile.

---

`65-67`: **Finally decided to flash your messages, huh?**  
I-it’s not like I’m praising your usage or anything!

</details>
<details>
<summary>server/routes/main.js (7)</summary>

`9-9`: **Importing Turnstile verification, are we?**  
I guess you really want to keep those bots away.

---

`145-147`: **You’re storing `captchaError` but never using it, you know.**  
What’s the point of this “tsun” message if it’s not shown anywhere? Check that logic, baka!

---

`158-161`: **Ah, so you flash your success, info, and error now…**  
Don’t get too full of yourself with all that user feedback.

---

`273-281`: **Verifying the user’s humanity with `verifyCloudflareTurnstileToken`?**  
Hmph, you must think you’re so clever!

---

`309-310`: **Logging out the commenter's name in production?**  
Don’t act like I care about these logs… but fine, be thorough.

---

`313-313`: **Flashing a success message upon comment submission!**  
Just don’t expect me to be impressed or anything!

---

`327-327`: **Updating the doc block?**  
Tsk, at least you’re clarifying your route usage properly.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-12T23:25:57Z
user: login: Debagnik
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-12T23:59:44Z
user: login: Debagnik
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-12T23:59:58Z

Review comments:
diff_hunk: @@ -102,6 +102,7 @@ She is Kurumi aka Walnut from Lycoris Recoil the mascot for this project.
         <link rel="stylesheet" href="/css/style.css">
         <script type="text/javascript" defer src="/js/script.js"></script>
         <title><%= locals.title %></title>
+        <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
path: views/layouts/main.ejs
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Don't think I'm impressed by your Cloudflare integration!**

Adding the Cloudflare Turnstile script is the bare minimum you could do! At least you remembered to use async and defer attributes, which will prevent blocking the page rendering.

Have you considered what happens if Cloudflare's service is down? It's not like I care if your users get stuck, but you should have some fallback mechanism!


---

🌐 Web query:

```web
What fallback options exist for Cloudflare Turnstile when the service is unavailable?
```

💡 Result:

Cloudflare Turnstile provides several fallback mechanisms and configurations to handle service unavailability or client-side errors, ensuring continuous operation even when challenges fail. Here are the key approaches:

## **Client-side error handling**
- **Error callbacks**: Implement `error-callback` to detect issues like network failures or unsupported browsers. This allows custom actions (e.g., displaying a user-friendly message) while Turnstile retries automatically[1][6].
  ```javascript
  turnstile.render('#widget', { 
    sitekey: 'your-key',
    error-callback: (errorCode) => { /* Handle error */ }
  });
  ```
- **Manual retries**: Use `turnstile.reset()` in the error callback to trigger a widget reload after a failure[1][2]. Configure retry intervals with `retry-interval` (default: 8 seconds)[1].

## **Server-side validation failsafes**
- **Fail-closed vs. fail-open**: Decide whether to block or allow requests if Turnstile's verification endpoint (`siteverify`) is unreachable. For critical actions (e.g., logins), fail-closed is recommended[9].
- **Token expiration checks**: Reject tokens older than 300 seconds to prevent stale challenges[4][9].

## **Alternative challenge mechanisms**
- **Interactive challenges**: For high-risk scenarios, configure Turnstile to force interactive puzzles via the `3x00000000000000000000FF` test key[4].
- **Fallback CAPTCHA**: Layer Turnstile with a secondary system (e.g., hCaptcha) for critical endpoints, activating it when Turnstile's solve rate drops below a threshold[7][9].

## **Configuration adjustments**
|| Default | Fallback Option |
|----------------|---------|-------------------|
| `retry`        | `auto`  | `never` to disable retries[1] |
| Widget type     | Non-interactive | Interactive mode via test key `3x00000000000000000000FF`[4] |

For enterprise users, combining Turnstile with Cloudflare's **Bot Management** provides additional resilience by analyzing request patterns when challenges fail[3]. Monitor the Turnstile dashboard for metrics like `Visitor Solve Rate` to detect systemic issues[7][10].

Citations:

- 1: https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/
- 2: https://developers.cloudflare.com/turnstile/troubleshooting/troubleshooting-faqs/
- 3: https://www.cloudflare.com/application-services/products/turnstile/
- 4: https://mediaron.com/developer-diaries-comments-and-cloudflare-turnstile/
- 5: https://blog.cloudflare.com/integrating-turnstile-with-the-cloudflare-waf-to-challenge-fetch-requests/
- 6: https://developers.cloudflare.com/turnstile/troubleshooting/challenge-solve-issues/
- 7: https://community.cloudflare.com/t/how-do-i-know-if-turnstile-is-working/728940
- 8: https://docs.capsolver.com/en/guide/captcha/cloudflare_turnstile/
- 9: https://www.troyhunt.com/fighting-api-bots-with-cloudflares-invisible-turnstile/
- 10: https://community.cloudflare.com/t/turnstile-issues/672463

---



</details>

**Hey, dummy—your Cloudflare Turnstile integration is still half-baked!**  
Honestly, it's not like I'm crying over your users getting stuck, but seriously, what happens when Cloudflare goes down? All you’ve done is load the script with `async` and `defer`—the bare minimum! You should totally consider implementing a proper fallback mechanism. For instance:

- **Implement an error callback:** When rendering your widget, use the `error-callback` to detect failures (like network issues) and perform actions—maybe even call `turnstile.reset()` to retry the challenge.
  ```javascript
  turnstile.render('#yourWidgetId', {
    sitekey: 'your-sitekey',
    error-callback: (errorCode) => {
      // Handle the error: display a friendly message or trigger a retry
      turnstile.reset(); // Retry the challenge manually
    }
  });
  ```
- **Server-side validation failsafes:** Consider your options when the verification endpoint is unreachable—decide between fail-closed or fail-open strategies to keep your users moving.
  
It’s not like I care if you take my advice, but seriously, add these fallbacks so your integration isn’t as fragile as it looks!

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-12T23:19:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -440,3 +440,49 @@ img {
 .comment-date {
 	font-size: 15px;
 }
+
+/** Flash Messaging Styles **/
+.flash-message {
+    position: relative;
+    padding: 1em;
+    margin-bottom: 1em;
+    border-radius: 10px;
+    font-weight: bold;
+    opacity: 1;
+    transition: opacity 0.5s ease-out;
+}
+
+.flash-error {
+    background-color: #ffe6e6;
+    border: 2px solid #ff4d4d;
+    color: #990000;
+}
+
+.flash-success {
+    background-color: #e6ffe6;
+    border: 2px solid #33cc33;
+    color: #006600;
+}
+
+.flash-info {
+    background-color: #cce7ff;
+    border: 2px solid #a1c6ea;
+    color: #0056b3;
+}
+
+.flash-message .close-btn {
+    position: absolute;
+    top: 5px;
+    right: 10px;
+    background: none;
+    border: none;
+    font-size: 1.2em;
+    cursor: pointer;
+    color: inherit;
+}
path: public/css/style.css
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**A close button that probably doesn't even work yet!**

You added styling for a close button, but I noticed those TODO comments in the template about fixing the close button functionality. Don't leave things half-finished, baka!

Complete the close button functionality by implementing the JavaScript to make it work. Something like:

```javascript
// Add to your script.js file
document.addEventListener('DOMContentLoaded', () => {
  const closeButtons = document.querySelectorAll('.flash-message .close-btn');
  closeButtons.forEach(button => {
    button.addEventListener('click', () => {
      const flashMessage = button.closest('.flash-message');
      flashMessage.classList.add('hide');
      setTimeout(() => {
        flashMessage.style.display = 'none';
      }, 500); // Match transition time in CSS
    });
  });
});
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-12T23:19:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -440,3 +440,49 @@ img {
 .comment-date {
 	font-size: 15px;
 }
+
+/** Flash Messaging Styles **/
+.flash-message {
+    position: relative;
+    padding: 1em;
+    margin-bottom: 1em;
+    border-radius: 10px;
+    font-weight: bold;
+    opacity: 1;
+    transition: opacity 0.5s ease-out;
+}
+
+.flash-error {
+    background-color: #ffe6e6;
+    border: 2px solid #ff4d4d;
+    color: #990000;
+}
+
+.flash-success {
+    background-color: #e6ffe6;
+    border: 2px solid #33cc33;
+    color: #006600;
+}
+
+.flash-info {
+    background-color: #cce7ff;
+    border: 2px solid #a1c6ea;
+    color: #0056b3;
+}
+
+.flash-message .close-btn {
+    position: absolute;
+    top: 5px;
+    right: 10px;
+    background: none;
+    border: none;
+    font-size: 1.2em;
+    cursor: pointer;
+    color: inherit;
+}
path: public/css/style.css
user: login: Debagnik
body: fixed
created_at: 2025-04-12T23:59:44Z
author_association: OWNER
diff_hunk: @@ -102,6 +102,7 @@ She is Kurumi aka Walnut from Lycoris Recoil the mascot for this project.
         <link rel="stylesheet" href="/css/style.css">
         <script type="text/javascript" defer src="/js/script.js"></script>
         <title><%= locals.title %></title>
+        <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
path: views/layouts/main.ejs
user: login: Debagnik
body: fixed
created_at: 2025-04-12T23:59:58Z
author_association: OWNER

PR comments:
user: login: Debagnik
created_at: 2025-04-12T23:19:18Z
body: @CoderabbitAI full review
author_association: OWNER
user: login: coderabbitai[bot]
created_at: 2025-04-12T23:19:23Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Full review triggered.

</details>
author_association: CONTRIBUTOR
user: login: Debagnik
created_at: 2025-04-12T23:58:58Z
body: fixes #67 
author_association: OWNER