From 155e747044d63332d458c7ec4f59321c01a1fc8d Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Wed, 3 Dec 2025 12:26:28 +0000 Subject: [PATCH] Add Express.js implementation with MySQL, EJS templates, and cookie-based authentication Co-authored-by: Roberto Luis Bisbe <825331+rlbisbe@users.noreply.github.com> --- README.md | 101 ++++++++++++++++++++++++++++++++ app.js | 110 +++++++++++++++++++++++++++++++++++ db.js | 43 ++++++++++++++ package.json | 20 +++++++ views/admin/add_new_post.ejs | 12 ++++ views/admin/index.ejs | 19 ++++++ views/admin/new_post.ejs | 12 ++++ views/index.ejs | 23 ++++++++ views/login.ejs | 12 ++++ views/logout.ejs | 8 +++ 10 files changed, 360 insertions(+) create mode 100644 README.md create mode 100644 app.js create mode 100644 db.js create mode 100644 package.json create mode 100644 views/admin/add_new_post.ejs create mode 100644 views/admin/index.ejs create mode 100644 views/admin/new_post.ejs create mode 100644 views/index.ejs create mode 100644 views/login.ejs create mode 100644 views/logout.ejs diff --git a/README.md b/README.md new file mode 100644 index 0000000..be1ec6e --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# PHPBlog Express.js Version + +This is a complete JavaScript/Express.js implementation of the phpblog application, replicating all functionality from the original PHP version. + +## Features + +- Express.js server with full routing +- MySQL database integration using mysql2 +- Cookie-based authentication +- EJS template engine for views +- All routes and functionality from the original PHP application + +## Prerequisites + +- Node.js (v14 or higher) +- MySQL server running on localhost +- Database 'blog' with table 'posts' (title, text columns) + +## Installation + +```bash +npm install +``` + +## Configuration + +The application connects to MySQL with the following credentials (matching the original PHP app): +- Host: localhost +- User: root +- Password: macbook +- Database: blog +- Table: posts + +## Database Setup + +Create the database and table if not already present: + +```sql +CREATE DATABASE IF NOT EXISTS blog; +USE blog; +CREATE TABLE IF NOT EXISTS posts ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + text TEXT NOT NULL +); +``` + +## Running the Application + +```bash +npm start +``` + +The server will start on http://localhost:3000 + +## Routes + +- `GET /` - Main blog page displaying all posts with login form +- `POST /login` - Login handler (username: roberto, password: password) +- `GET /logout` - Logout and clear session +- `GET /admin` - Admin panel to view all posts (requires authentication) +- `GET /admin/new_post` - Form to create new post (requires authentication) +- `POST /admin/add_new_post` - Handler to add new post (requires authentication) + +## Authentication + +- Username: `roberto` +- Password: `password` (MD5 hash: 3bc2e28ca8940090c3d80c851784a5d5) +- Uses cookie-based authentication matching the original PHP implementation + +## Project Structure + +``` +phpblog/ +├── app.js # Main Express application +├── db.js # Database layer (MySQL connection and queries) +├── package.json # Node.js dependencies +├── views/ # EJS templates +│ ├── index.ejs # Main blog page +│ ├── login.ejs # Login result page +│ ├── logout.ejs # Logout confirmation page +│ └── admin/ +│ ├── index.ejs # Admin panel +│ ├── new_post.ejs # New post form +│ └── add_new_post.ejs # Post creation result +└── [PHP files] # Original PHP implementation +``` + +## Key Implementation Details + +### Backend Functions (db.js) +- `init(callback)` - Fetches all posts from the database +- `addNewPost(title, text, callback)` - Inserts a new post into the database + +### Middleware +- `requireAuth` - Protects admin routes by checking for username cookie +- `body-parser` - Parses form data +- `cookie-parser` - Handles cookie parsing and setting + +### Views +All EJS templates replicate the exact HTML structure and behavior of the original PHP pages. diff --git a/app.js b/app.js new file mode 100644 index 0000000..c8928ea --- /dev/null +++ b/app.js @@ -0,0 +1,110 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const cookieParser = require('cookie-parser'); +const crypto = require('crypto'); +const db = require('./db'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Middleware +app.use(bodyParser.urlencoded({ extended: true })); +app.use(cookieParser()); +app.set('view engine', 'ejs'); +app.set('views', './views'); + +// Global variables and functions +const system = "Subtitulo de la aplicacion"; + +function theTitle() { + return "Titulo de la aplicacion"; +} + +// Authentication middleware +function requireAuth(req, res, next) { + if (!req.cookies.username) { + res.send("You shouldn't be here"); + } else { + next(); + } +} + +// Routes + +// Main blog page +app.get('/', (req, res) => { + db.init((error, posts) => { + if (error) { + res.status(500).send('Database error'); + return; + } + res.render('index', { + title: theTitle(), + system: system, + posts: posts, + username: req.cookies.username + }); + }); +}); + +// Login page (POST) +app.post('/login', (req, res) => { + const username = "roberto"; + const password = "3bc2e28ca8940090c3d80c851784a5d5"; // MD5 of "password" + + const userInput = req.body.user; + const passwordInput = req.body.password; + const passwordHash = crypto.createHash('md5').update(passwordInput).digest('hex'); + + let logged = false; + if (userInput === username && passwordHash === password) { + logged = true; + res.cookie('username', 'roberto'); + } + + res.render('login', { logged: logged }); +}); + +// Logout page +app.get('/logout', (req, res) => { + res.clearCookie('username'); + res.render('logout'); +}); + +// Admin panel - list all posts +app.get('/admin', requireAuth, (req, res) => { + db.init((error, posts) => { + if (error) { + res.status(500).send('Database error'); + return; + } + res.render('admin/index', { + username: req.cookies.username, + posts: posts + }); + }); +}); + +// New post form +app.get('/admin/new_post', requireAuth, (req, res) => { + res.render('admin/new_post'); +}); + +// Add new post handler +app.post('/admin/add_new_post', requireAuth, (req, res) => { + const title = req.body.title; + const text = req.body.text; + + db.addNewPost(title, text, (error, results) => { + res.render('admin/add_new_post', { + title: title, + text: text, + error: error + }); + }); +}); + +// Start server +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); diff --git a/db.js b/db.js new file mode 100644 index 0000000..a04853a --- /dev/null +++ b/db.js @@ -0,0 +1,43 @@ +const mysql = require('mysql2'); + +function createConnection() { + return mysql.createConnection({ + host: 'localhost', + user: 'root', + password: 'macbook', + database: 'blog' + }); +} + +function init(callback) { + const connection = createConnection(); + connection.query('SELECT * FROM posts', (error, results) => { + connection.end(); + if (error) { + callback(error, null); + } else { + callback(null, results); + } + }); +} + +function addNewPost(title, text, callback) { + const connection = createConnection(); + connection.query( + 'INSERT INTO posts (title, text) VALUES (?, ?)', + [title, text], + (error, results) => { + connection.end(); + if (error) { + callback(error, null); + } else { + callback(null, results); + } + } + ); +} + +module.exports = { + init, + addNewPost +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..9ba813e --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "phpblog-express", + "version": "1.0.0", + "description": "Express.js version of phpblog application", + "main": "app.js", + "scripts": { + "start": "node app.js", + "dev": "nodemon app.js" + }, + "keywords": ["express", "blog", "mysql"], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "ejs": "^3.1.9", + "mysql2": "^3.6.5", + "cookie-parser": "^1.4.6", + "body-parser": "^1.20.2" + } +} diff --git a/views/admin/add_new_post.ejs b/views/admin/add_new_post.ejs new file mode 100644 index 0000000..822cbcf --- /dev/null +++ b/views/admin/add_new_post.ejs @@ -0,0 +1,12 @@ + + + + +

Title: <%= title %>

+

Text: <%= text %>

+ <% if (error) { %> +

Error: <%= error.message %>

+ <% } %> + Go to admin + + diff --git a/views/admin/index.ejs b/views/admin/index.ejs new file mode 100644 index 0000000..785492a --- /dev/null +++ b/views/admin/index.ejs @@ -0,0 +1,19 @@ + + + + + +

Welcome <%= username %> Go home

+ +

Listing posts

+ + + <% posts.forEach(function(row) { %> + + <% }); %> +
TitleActions
<%= row.title %>Edit | Delete
+

Options

+ New post
+ Log out + + diff --git a/views/admin/new_post.ejs b/views/admin/new_post.ejs new file mode 100644 index 0000000..8a4af09 --- /dev/null +++ b/views/admin/new_post.ejs @@ -0,0 +1,12 @@ + + + + +

New Post

+
+
+
+ +
+ + diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..8435eaf --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,23 @@ + + + + +

<%= title %>

+

<%= system %>

+<% posts.forEach(function(row) { %> +

<%= row.title %>

+

<%= row.text %>

+<% }); %> +

Meta

+<% if (!username) { %> +
+
+
+ +
+<% } else { %> +

Logged in as <%= username %>. Go to admin

+<% } %> + + + diff --git a/views/login.ejs b/views/login.ejs new file mode 100644 index 0000000..b8129ab --- /dev/null +++ b/views/login.ejs @@ -0,0 +1,12 @@ + + + + + <% if (logged === true) { %> +

Logged in

+ Go to admin + <% } else { %> +

Login error

+ <% } %> + + diff --git a/views/logout.ejs b/views/logout.ejs new file mode 100644 index 0000000..b96ceac --- /dev/null +++ b/views/logout.ejs @@ -0,0 +1,8 @@ + + + + +

You have been succesfully logged out

+ Go to index + +