Skip to content

Commit 13660c6

Browse files
Merge pull request #1 from Andrew-web-dev-coder/lab2-work-with-API
Lab2 work with api
2 parents 983d9f0 + 6927451 commit 13660c6

File tree

33 files changed

+4034
-0
lines changed

33 files changed

+4034
-0
lines changed

.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Node modules
2+
node_modules/
3+
frontend/node_modules/
4+
backend/node_modules/
5+
6+
# Logs
7+
logs
8+
*.log
9+
npm-debug.log*
10+
yarn-debug.log*
11+
yarn-error.log*
12+
13+
# Dependency directories
14+
jspm_packages/
15+
16+
# Build outputs
17+
dist/
18+
build/
19+
frontend/dist/
20+
backend/dist/
21+
22+
# Environment variables
23+
.env
24+
.env.local
25+
.env.development.local
26+
.env.test.local
27+
.env.production.local
28+
29+
# System files
30+
.DS_Store
31+
Thumbs.db
32+
33+
# IDE settings
34+
.vscode/
35+
.idea/
36+
*.swp
37+
38+
# Backup files
39+
*.bak
40+
*.tmp
41+

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,64 @@
1+
🚗 Auto Articles — Full-Stack Node.js + React App
2+
- Overview
13

4+
A simple full-stack application for managing car-related articles.
5+
Users can:
6+
7+
View a list of existing articles
8+
9+
Open and read a specific article
10+
11+
Create new articles using a WYSIWYG editor
12+
13+
The frontend is built with React + Vite,
14+
and the backend uses Node.js + Express, storing data as JSON files.
15+
16+
* Tech Stack
17+
18+
Frontend: React, React Router, React Quill, Vite
19+
Backend: Node.js, Express, FS
20+
Other: concurrently (to run both servers together)
21+
22+
* Setup & Run Instructions
23+
24+
Install dependencies
25+
26+
npm install
27+
28+
29+
Run only the backend
30+
31+
npm run start:backend
32+
33+
34+
Backend server runs at http://localhost:4000
35+
36+
Run only the frontend
37+
38+
npm run start:frontend
39+
40+
41+
App starts at http://localhost:5173
42+
43+
(Vite may choose another port if it’s already in use)
44+
45+
Run both frontend and backend together
46+
47+
npm run start:all
48+
49+
50+
Both servers start concurrently using concurrently.
51+
52+
* Validation & Error Handling
53+
54+
Backend validates title and content (required and minimum length).
55+
56+
Returns 400 Bad Request with a JSON error if validation fails.
57+
58+
Frontend shows user-friendly messages for errors instead of plain alerts.
59+
60+
* API Endpoints
61+
Method Route Description
62+
GET /articles Get all saved articles
63+
GET /articles/:id Get a specific article by ID
64+
POST /articles Create a new article

apps/log-analyzer/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#!/usr/bin/env node

apps/log-generator/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env node
2+
import Logger from '../../shared/logger.js';
3+
const logger = new Logger();
4+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const ArticleService = require('../services/articleService');
2+
const sendJson = require('../utils/sendJson');
3+
4+
exports.getAllArticles = (req, res) => {
5+
try {
6+
const articles = ArticleService.getAll();
7+
return sendJson(res, 200, articles);
8+
} catch (err) {
9+
console.error(err);
10+
return sendJson(res, 500, { error: 'Failed to read articles' });
11+
}
12+
};
13+
14+
exports.getArticleById = (req, res) => {
15+
try {
16+
const article = ArticleService.getById(req.params.id);
17+
18+
if (!article) {
19+
return sendJson(res, 404, { error: 'Article not found' });
20+
}
21+
22+
return sendJson(res, 200, article);
23+
} catch (err) {
24+
console.error(err);
25+
return sendJson(res, 500, { error: 'Failed to read article' });
26+
}
27+
};
28+
29+
exports.createArticle = (req, res) => {
30+
try {
31+
let { title, content } = req.body;
32+
33+
34+
title = typeof title === 'string' ? title.trim() : '';
35+
content = typeof content === 'string' ? content.trim() : '';
36+
37+
if (!title || !content) {
38+
return sendJson(res, 400, { error: 'Title and content are required.' });
39+
}
40+
41+
42+
if (title.length < 3) {
43+
return sendJson(res, 400, { error: 'Title must be at least 3 characters long.' });
44+
}
45+
46+
const article = ArticleService.create({ title, content });
47+
return sendJson(res, 201, article);
48+
} catch (err) {
49+
console.error(err);
50+
51+
return sendJson(res, 400, { error: err.message || 'Failed to create article.' });
52+
}
53+
};

article-app/backend/data/1.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"id": 1,
3+
"title": "BMW M3 E46",
4+
"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.",
5+
"createdAt": "2025-11-09T02:24:29.094Z"
6+
}

article-app/backend/data/2.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"id": 2,
3+
"title": "Audi RS6 Avant",
4+
"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.",
5+
"createdAt": "2025-11-09T02:24:29.096Z"
6+
}

article-app/backend/data/3.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"id": 3,
3+
"title": "Toyota Supra A80",
4+
"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.",
5+
"createdAt": "2025-11-09T02:24:29.096Z"
6+
}

0 commit comments

Comments
 (0)