Adding README.md File
هذا الالتزام موجود في:
176
README .md
Normal file
176
README .md
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
# 🍎 Fruit Store API
|
||||||
|
|
||||||
|
A simple **RESTful API** built with Flask that manages fruits in a store.
|
||||||
|
The project also includes **logging**, a **log monitoring script**, and **automated testing script**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Features
|
||||||
|
- CRUD operations on fruits (`GET`, `POST`, `PUT`, `DELETE`)
|
||||||
|
- Search fruits by name
|
||||||
|
- Filter fruits by category
|
||||||
|
- Health check endpoint
|
||||||
|
- Logging of all requests & responses to `api.log`
|
||||||
|
- Bash script for monitoring logs (`monitor.sh`)
|
||||||
|
- Bash script for automated API testing (`test_api.sh`)
|
||||||
|
- Swagger/OpenAPI documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Setup Instructions
|
||||||
|
|
||||||
|
### 1. Clone the Repository
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/YOUR-USERNAME/fruit-api.git
|
||||||
|
cd fruit-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create Virtual Environment & Install Dependencies
|
||||||
|
```bash
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate # Linux/Mac
|
||||||
|
venv\Scripts\activate # Windows
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run the API
|
||||||
|
```bash
|
||||||
|
python fruit_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
API will start at:
|
||||||
|
👉 `http://localhost:5000`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 API Endpoints
|
||||||
|
|
||||||
|
### Base URL
|
||||||
|
```
|
||||||
|
http://localhost:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------------------------------|------------------------------|
|
||||||
|
| GET | `/fruits` | Get all fruits |
|
||||||
|
| GET | `/fruits/<id>` | Get fruit by ID |
|
||||||
|
| POST | `/fruits` | Add a new fruit |
|
||||||
|
| PUT | `/fruits/<id>` | Update an existing fruit |
|
||||||
|
| DELETE | `/fruits/<id>` | Delete a fruit |
|
||||||
|
| GET | `/fruits/category/<category>` | Get fruits by category |
|
||||||
|
| GET | `/fruits/search?name=<name>` | Search fruits by name |
|
||||||
|
| GET | `/health` | Health check |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📑 Example Usage (with curl)
|
||||||
|
|
||||||
|
- **Get all fruits**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:5000/fruits
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Add a fruit**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:5000/fruits -H "Content-Type: application/json" -d '{"name":"Mango","color":"Yellow","price":2.5,"quantity":50,"category":"Tropical"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Update a fruit**
|
||||||
|
```bash
|
||||||
|
curl -X PUT http://localhost:5000/fruits/1 -H "Content-Type: application/json" -d '{"name":"Green Apple","price":1.8}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Logging
|
||||||
|
|
||||||
|
- All API requests and responses are saved in:
|
||||||
|
```
|
||||||
|
api.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Example log:
|
||||||
|
```
|
||||||
|
2025-09-29 18:05:22 - IP:127.0.0.1 - METHOD:GET - ENDPOINT:/fruits - STATUS:200
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔎 Monitoring (monitor.sh)
|
||||||
|
|
||||||
|
The `monitor.sh` script:
|
||||||
|
- Monitors `api.log` every 2 hours
|
||||||
|
- Detects **errors (4XX, 5XX)** and unusual keywords (`failed`, `timeout`)
|
||||||
|
- Captures server metrics (IP, CPU, RAM, Disk)
|
||||||
|
- Sends an **alert JSON** to Ghaymah endpoint
|
||||||
|
|
||||||
|
Run it in background:
|
||||||
|
```bash
|
||||||
|
bash monitor.sh &
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤖 API Testing (test_api.sh)
|
||||||
|
|
||||||
|
The `test_api.sh` script:
|
||||||
|
- Runs automated `GET`, `POST`, `PUT`, `DELETE` requests
|
||||||
|
- Logs results in `test_results.log`
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
```bash
|
||||||
|
bash test_api.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📘 Swagger Documentation
|
||||||
|
After starting the API, open:
|
||||||
|
👉 [http://localhost:5000/docs](http://localhost:5000/docs)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker Support
|
||||||
|
|
||||||
|
### Build the image
|
||||||
|
```bash
|
||||||
|
docker build -t fruit-api .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run container
|
||||||
|
```bash
|
||||||
|
docker run -p 5000:5000 fruit-api
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Project Structure
|
||||||
|
```
|
||||||
|
fruit-api/
|
||||||
|
│── fruit_api.py # Main Flask API
|
||||||
|
│── requirements.txt # Dependencies
|
||||||
|
│── Dockerfile # Docker support
|
||||||
|
│── monitor.sh # Log monitoring script
|
||||||
|
│── test_api.sh # Automated testing script
|
||||||
|
│── api.log # API logs
|
||||||
|
│── test_results.log # Test logs
|
||||||
|
│── README.md # Documentation
|
||||||
|
│── .gitignore # Ignore venv/logs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Submission Guidelines
|
||||||
|
- Push project to **GitPasha**: [https://app.gitpasha.com/](https://app.gitpasha.com/)
|
||||||
|
- Repository must include:
|
||||||
|
- API source code
|
||||||
|
- Bash scripts (`monitor.sh`, `test_api.sh`)
|
||||||
|
- Sample logs
|
||||||
|
- Documentation (`README.md`)
|
||||||
|
- Demo should include:
|
||||||
|
- Running the API
|
||||||
|
- Triggering errors
|
||||||
|
- Showing alerts in action
|
@@ -54,3 +54,76 @@
|
|||||||
2025-09-27 23:55:36,704 [INFO] REQUEST: method=GET path=/ args={} body=None ip=192.168.43.139
|
2025-09-27 23:55:36,704 [INFO] REQUEST: method=GET path=/ args={} body=None ip=192.168.43.139
|
||||||
2025-09-27 23:55:36,704 [INFO] RESPONSE: method=GET path=/ status=200 ip=192.168.43.139
|
2025-09-27 23:55:36,704 [INFO] RESPONSE: method=GET path=/ status=200 ip=192.168.43.139
|
||||||
2025-09-27 23:55:36,704 [INFO] 192.168.43.139 - - [27/Sep/2025 23:55:36] "GET / HTTP/1.1" 200 -
|
2025-09-27 23:55:36,704 [INFO] 192.168.43.139 - - [27/Sep/2025 23:55:36] "GET / HTTP/1.1" 200 -
|
||||||
|
2025-09-28 15:21:56,247 [INFO] [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||||
|
* Running on all addresses (0.0.0.0)
|
||||||
|
* Running on http://127.0.0.1:5000
|
||||||
|
* Running on http://192.168.43.139:5000
|
||||||
|
2025-09-28 15:21:56,258 [INFO] [33mPress CTRL+C to quit[0m
|
||||||
|
2025-09-28 15:22:01,830 [INFO] REQUEST: method=GET path=/ args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-28 15:22:01,830 [INFO] RESPONSE: method=GET path=/ status=200 ip=127.0.0.1
|
||||||
|
2025-09-28 15:22:01,831 [INFO] 127.0.0.1 - - [28/Sep/2025 15:22:01] "GET / HTTP/1.1" 200 -
|
||||||
|
2025-09-28 15:22:03,048 [INFO] REQUEST: method=GET path=/favicon.ico args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-28 15:22:03,049 [INFO] RESPONSE: method=GET path=/favicon.ico status=404 ip=127.0.0.1
|
||||||
|
2025-09-28 15:22:03,049 [INFO] 127.0.0.1 - - [28/Sep/2025 15:22:03] "[33mGET /favicon.ico HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:49:26,854 [INFO] [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||||
|
* Running on all addresses (0.0.0.0)
|
||||||
|
* Running on http://127.0.0.1:5000
|
||||||
|
* Running on http://192.168.43.139:5000
|
||||||
|
2025-09-29 19:49:26,865 [INFO] [33mPress CTRL+C to quit[0m
|
||||||
|
2025-09-29 19:49:31,949 [INFO] REQUEST: method=GET path=/ args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:31,950 [INFO] RESPONSE: method=GET path=/ status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:31,951 [INFO] 127.0.0.1 - - [29/Sep/2025 19:49:31] "GET / HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:49:32,958 [INFO] REQUEST: method=GET path=/favicon.ico args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:32,959 [INFO] RESPONSE: method=GET path=/favicon.ico status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:32,960 [INFO] 127.0.0.1 - - [29/Sep/2025 19:49:32] "[33mGET /favicon.ico HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:49:39,141 [INFO] REQUEST: method=GET path=/docs/ args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:39,142 [INFO] RESPONSE: method=GET path=/docs/ status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:39,143 [INFO] 127.0.0.1 - - [29/Sep/2025 19:49:39] "[33mGET /docs/ HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:49:49,569 [INFO] REQUEST: method=GET path=/ args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:49,570 [INFO] RESPONSE: method=GET path=/ status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:49,570 [INFO] 127.0.0.1 - - [29/Sep/2025 19:49:49] "GET / HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:49:49,656 [INFO] REQUEST: method=GET path=/favicon.ico args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:49,657 [INFO] RESPONSE: method=GET path=/favicon.ico status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:49:49,657 [INFO] 127.0.0.1 - - [29/Sep/2025 19:49:49] "[33mGET /favicon.ico HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:51:31,227 [INFO] REQUEST: method=GET path=/items args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:51:31,228 [INFO] RESPONSE: method=GET path=/items status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:51:31,228 [INFO] 127.0.0.1 - - [29/Sep/2025 19:51:31] "[33mGET /items HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:51:51,563 [INFO] REQUEST: method=GET path=/items/1 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:51:51,563 [INFO] RESPONSE: method=GET path=/items/1 status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:51:51,564 [INFO] 127.0.0.1 - - [29/Sep/2025 19:51:51] "[33mGET /items/1 HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:52:37,279 [INFO] REQUEST: method=GET path=/fruits args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:52:37,279 [INFO] RESPONSE: method=GET path=/fruits status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:52:37,280 [INFO] 127.0.0.1 - - [29/Sep/2025 19:52:37] "GET /fruits HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:53:04,750 [INFO] REQUEST: method=GET path=/fruit args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:04,751 [INFO] RESPONSE: method=GET path=/fruit status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:04,752 [INFO] 127.0.0.1 - - [29/Sep/2025 19:53:04] "[33mGET /fruit HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:53:15,728 [INFO] REQUEST: method=GET path=/items/1 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:15,728 [INFO] RESPONSE: method=GET path=/items/1 status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:15,729 [INFO] 127.0.0.1 - - [29/Sep/2025 19:53:15] "[33mGET /items/1 HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:53:23,708 [INFO] REQUEST: method=GET path=/fruit/1 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:23,709 [INFO] RESPONSE: method=GET path=/fruit/1 status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:23,709 [INFO] 127.0.0.1 - - [29/Sep/2025 19:53:23] "[33mGET /fruit/1 HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:53:37,805 [INFO] REQUEST: method=GET path=/fruits/1 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:37,805 [INFO] RESPONSE: method=GET path=/fruits/1 status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:37,806 [INFO] 127.0.0.1 - - [29/Sep/2025 19:53:37] "GET /fruits/1 HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:53:51,028 [INFO] REQUEST: method=GET path=/fruits args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:51,029 [INFO] RESPONSE: method=GET path=/fruits status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:51,030 [INFO] 127.0.0.1 - - [29/Sep/2025 19:53:51] "GET /fruits HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:53:59,807 [INFO] REQUEST: method=GET path=/fruits/1 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:59,807 [INFO] RESPONSE: method=GET path=/fruits/1 status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:53:59,808 [INFO] 127.0.0.1 - - [29/Sep/2025 19:53:59] "GET /fruits/1 HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:54:05,916 [INFO] REQUEST: method=GET path=/fruits/2 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:05,917 [INFO] RESPONSE: method=GET path=/fruits/2 status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:05,917 [INFO] 127.0.0.1 - - [29/Sep/2025 19:54:05] "GET /fruits/2 HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:54:10,593 [INFO] REQUEST: method=GET path=/fruits/3 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:10,594 [INFO] RESPONSE: method=GET path=/fruits/3 status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:10,594 [INFO] 127.0.0.1 - - [29/Sep/2025 19:54:10] "GET /fruits/3 HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:54:14,309 [INFO] REQUEST: method=GET path=/fruits/4 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:14,310 [INFO] RESPONSE: method=GET path=/fruits/4 status=404 ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:14,310 [INFO] 127.0.0.1 - - [29/Sep/2025 19:54:14] "[33mGET /fruits/4 HTTP/1.1[0m" 404 -
|
||||||
|
2025-09-29 19:54:57,939 [INFO] REQUEST: method=GET path=/fruits/1 args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:57,939 [INFO] RESPONSE: method=GET path=/fruits/1 status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:54:57,940 [INFO] 127.0.0.1 - - [29/Sep/2025 19:54:57] "GET /fruits/1 HTTP/1.1" 200 -
|
||||||
|
2025-09-29 19:55:02,233 [INFO] REQUEST: method=GET path=/fruits args={} body=None ip=127.0.0.1
|
||||||
|
2025-09-29 19:55:02,233 [INFO] RESPONSE: method=GET path=/fruits status=200 ip=127.0.0.1
|
||||||
|
2025-09-29 19:55:02,234 [INFO] 127.0.0.1 - - [29/Sep/2025 19:55:02] "GET /fruits HTTP/1.1" 200 -
|
||||||
|
المرجع في مشكلة جديدة
حظر مستخدم