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
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Node modules
node_modules/
frontend/node_modules/
backend/node_modules/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Dependency directories
jspm_packages/

# Build outputs
dist/
build/
frontend/dist/
backend/dist/

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# System files
.DS_Store
Thumbs.db

# IDE settings
.vscode/
.idea/
*.swp

# Backup files
*.bak
*.tmp

63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,64 @@
🚗 Auto Articles — Full-Stack Node.js + React App
- Overview

A simple full-stack application for managing car-related articles.
Users can:

View a list of existing articles

Open and read a specific article

Create new articles using a WYSIWYG editor

The frontend is built with React + Vite,
and the backend uses Node.js + Express, storing data as JSON files.

* Tech Stack

Frontend: React, React Router, React Quill, Vite
Backend: Node.js, Express, FS
Other: concurrently (to run both servers together)

* Setup & Run Instructions

Install dependencies

npm install


Run only the backend

npm run start:backend


Backend server runs at http://localhost:4000

Run only the frontend

npm run start:frontend


App starts at http://localhost:5173

(Vite may choose another port if it’s already in use)

Run both frontend and backend together

npm run start:all


Both servers start concurrently using concurrently.

* Validation & Error Handling

Backend validates title and content (required and minimum length).

Returns 400 Bad Request with a JSON error if validation fails.

Frontend shows user-friendly messages for errors instead of plain alerts.

* API Endpoints
Method Route Description
GET /articles Get all saved articles
GET /articles/:id Get a specific article by ID
POST /articles Create a new article
1 change: 1 addition & 0 deletions apps/log-analyzer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#!/usr/bin/env node
4 changes: 4 additions & 0 deletions apps/log-generator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node
import Logger from '../../shared/logger.js';
const logger = new Logger();

53 changes: 53 additions & 0 deletions article-app/backend/controllers/articleController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const ArticleService = require('../services/articleService');
const sendJson = require('../utils/sendJson');

exports.getAllArticles = (req, res) => {
try {
const articles = ArticleService.getAll();
return sendJson(res, 200, articles);
} catch (err) {
console.error(err);
return sendJson(res, 500, { error: 'Failed to read articles' });
}
};

exports.getArticleById = (req, res) => {
try {
const article = ArticleService.getById(req.params.id);

if (!article) {
return sendJson(res, 404, { error: 'Article not found' });
}

return sendJson(res, 200, article);
} catch (err) {
console.error(err);
return sendJson(res, 500, { error: 'Failed to read article' });
}
};

exports.createArticle = (req, res) => {
try {
let { title, content } = req.body;


title = typeof title === 'string' ? title.trim() : '';
content = typeof content === 'string' ? content.trim() : '';

if (!title || !content) {
return sendJson(res, 400, { error: 'Title and content are required.' });
}


if (title.length < 3) {
return sendJson(res, 400, { error: 'Title must be at least 3 characters long.' });
}

const article = ArticleService.create({ title, content });
return sendJson(res, 201, article);
} catch (err) {
console.error(err);

return sendJson(res, 400, { error: err.message || 'Failed to create article.' });
}
};
6 changes: 6 additions & 0 deletions article-app/backend/data/1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": 1,
"title": "BMW M3 E46",
"content": "The BMW M3 E46 is a legendary sports car from the early 2000s, powered by the naturally aspirated S54 engine paired with a manual gearbox. It became an icon for its perfect balance of performance and handling.",
"createdAt": "2025-11-09T02:24:29.094Z"
}
6 changes: 6 additions & 0 deletions article-app/backend/data/2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": 2,
"title": "Audi RS6 Avant",
"content": "The Audi RS6 Avant is a high-performance wagon combining luxury and raw power. With its twin-turbo V8 and quattro all-wheel drive, it blends family practicality with sports car performance.",
"createdAt": "2025-11-09T02:24:29.096Z"
}
6 changes: 6 additions & 0 deletions article-app/backend/data/3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"id": 3,
"title": "Toyota Supra A80",
"content": "The Toyota Supra A80 is an iconic Japanese sports car from the 1990s, famous for its 2JZ-GTE turbocharged engine and incredible tuning potential. It became a global legend after appearing in the Fast & Furious movies.",
"createdAt": "2025-11-09T02:24:29.096Z"
}
Loading