Skip to content
Open
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
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
110 changes: 110 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -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}`);
});
43 changes: 43 additions & 0 deletions db.js
Original file line number Diff line number Diff line change
@@ -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
};
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
12 changes: 12 additions & 0 deletions views/admin/add_new_post.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head>
</head>
<body>
<p>Title: <%= title %></p>
<p>Text: <%= text %></p>
<% if (error) { %>
<p>Error: <%= error.message %></p>
<% } %>
<a href='/admin'>Go to admin</a>
</body>
</html>
19 changes: 19 additions & 0 deletions views/admin/index.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<html>
<head>

</head>
<body>
<p>Welcome <%= username %> <a href='/'>Go home</a></p>

<h1>Listing posts</h1>
<table border="0" cellspacing="5" cellpadding="5">
<tr><th>Title</th><th>Actions</th></tr>
<% posts.forEach(function(row) { %>
<tr><td><%= row.title %></td><td><a href=''>Edit</a> | <a href=''>Delete</a></tr>
<% }); %>
</table>
<h1>Options</h1>
<a href='/admin/new_post'>New post</a><br/>
<a href='/logout'>Log out</a>
</body>
</html>
12 changes: 12 additions & 0 deletions views/admin/new_post.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head>
</head>
<body>
<h1>New Post</h1>
<form action="/admin/add_new_post" method="post">
<label for="title">Title:</label> <input type="text" name="title"></input><br/>
<label for="text">Text:</label> <textarea name="text" rows="8" cols="40"></textarea><br/>
<input type="submit"/>
</form>
</body>
</html>
23 changes: 23 additions & 0 deletions views/index.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>
<head>
</head>
<body>
<h1><%= title %></h1>
<h2><%= system %></h2>
<% posts.forEach(function(row) { %>
<h1><%= row.title %></h1>
<p><%= row.text %></p>
<% }); %>
<h3>Meta</h3>
<% if (!username) { %>
<form action="/login" method="post">
<label for="user">User:</label> <input type="text" name="user"></input><br/>
<label for="user">Password:</label> <input type="password" name="password"></input><br/>
<input type="submit"/>
</form>
<% } else { %>
<p>Logged in as <%= username %>. <a href="/admin">Go to admin</a></p>
<% } %>

</body>
</html>
12 changes: 12 additions & 0 deletions views/login.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head>
</head>
<body>
<% if (logged === true) { %>
<h1>Logged in</h1>
<a href="/admin">Go to admin</a>
<% } else { %>
<h1>Login error</h1>
<% } %>
</body>
</html>
8 changes: 8 additions & 0 deletions views/logout.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<h1>You have been succesfully logged out</h1>
<a href="/">Go to index</a>
</body>
</html>