diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..70121784fe --- /dev/null +++ b/.flake8 @@ -0,0 +1,13 @@ +[flake8] +max-line-length = 100 +extend-ignore = E203,W503 +exclude = + */migrations/*, + server/frontend/*, + */venv/*, + */__pycache__/*, + */node_modules/*, + */build/*, + */dist/*, + */static/*, + */media/* \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..a504a4c739 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,60 @@ +name: Lint Code + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +jobs: + lint_python: + name: Lint Python + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Instalar flake8 + run: | + python -m pip install --upgrade pip + pip install flake8 + + - name: Ejecutar flake8 en todos los .py + run: | + find . -type f -name "*.py" -exec flake8 {} + + + lint_js: + name: Lint JavaScript & JSX + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Instalar Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Instalar JSHint global y dependencias frontend + run: | + npm install -g jshint + cd server/frontend + npm ci + + - name: Ejecutar JSHint solo sobre JS "legacy" (sin JSX) + run: | + echo "▶️ Ejecutando JSHint en server/database/*.js" + find server/database -type f -name "*.js" -exec jshint {} + + echo "✔ JSHint completado en server/database/*.js" + + - name: Ejecutar ESLint sobre React/JSX moderno + run: | + echo "▶️ Ejecutando ESLint en server/frontend/src/**/*.{js,jsx}" + cd server/frontend + npx eslint "src/**/*.{js,jsx}" --max-warnings=0 + echo "✔ ESLint completado en server/frontend/src/**/*.{js,jsx}" \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..5e626e8c81 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,68 @@ +name: Lint Code + +on: + push: + branches: [feature/review-system] + pull_request: + branches: [feature/review-system] + +jobs: + # ─────────────────────────────────────────── + # 1) LINT PYTHON + # ─────────────────────────────────────────── + lint_python: + name: Lint Python Files + runs-on: ubuntu-latest + + steps: + # 1.1 Clona el repo + - name: Checkout Repository + uses: actions/checkout@v3 + + # 1.2 Prepara Python 3.12 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + # 1.3 Instala flake8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 + + # 1.4 Ejecuta flake8 en TODO el repo (excluye la carpeta node_modules si existiera) + - name: Run flake8 + run: | + echo "Running flake8…" + find . -path "./server/frontend/node_modules" -prune -o -name "*.py" -exec flake8 {} + + echo "✅ Python lint passed" + + # ─────────────────────────────────────────── + # 2) LINT JAVASCRIPT / JSX + # ─────────────────────────────────────────── + lint_js: + name: Lint JavaScript Files + runs-on: ubuntu-latest + + steps: + # 2.1 Clona el repo + - name: Checkout Repository + uses: actions/checkout@v3 + + # 2.2 Prepara Node 14 (puedes subir a 18 o 20 si lo prefieres) + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 14 + + # 2.3 Instala JSHint globalmente + - name: Install JSHint + run: npm install --global jshint + + # 2.4 Ejecuta JSHint sobre todo tu frontend (evita node_modules) + - name: Run JSHint + run: | + echo "Running JSHint…" + find ./server/frontend \( -path "./server/frontend/node_modules" -prune \) -o -name "*.js" -o -name "*.jsx" -exec jshint {} + + echo "✅ JavaScript lint passed" diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000000..e5ef720060 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,4 @@ +# Ignorar carpeta entera de frontend (y cualquier otro directorio que no quieras que JSHint toque) +server/frontend/** +server/djangoapp/** # (opcional: si no tienes .js legacy dentro de djangoapp) +*.jsx # ignora cualquier archivo con extensión .jsx \ No newline at end of file diff --git a/README.md b/README.md index 5884e26a5b..a0614263df 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ -# coding-project-template \ No newline at end of file +# coding-project-template +[![Lint Code](https://github.com/lukaslondono77/xrwvm-fullstack_developer_capstone/actions/workflows/main.yml/badge.svg?branch=feature%2Freview-system)](https://github.com/lukaslondono77/xrwvm-fullstack_developer_capstone/actions/workflows/main.yml) diff --git a/server/.flake8 b/server/.flake8 new file mode 100644 index 0000000000..2cea0906cc --- /dev/null +++ b/server/.flake8 @@ -0,0 +1,6 @@ +[flake8] +max-line-length = 79 +extend-ignore = E203 +exclude = .git,__pycache__,build,dist +per-file-ignores = + __init__.py: F401 \ No newline at end of file diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000000..d03011f899 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.12.0-slim-bookworm + +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +ENV APP=/app + +WORKDIR $APP + +COPY requirements.txt $APP/ +RUN pip3 install --upgrade pip && \ + pip3 install -r requirements.txt + +COPY . $APP + +EXPOSE 8000 + +RUN chmod +x /app/entrypoint.sh + +ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"] + +CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "djangoproj.wsgi"] diff --git a/server/database/app.js b/server/database/app.js index 00f52b2008..be6a56bb80 100644 --- a/server/database/app.js +++ b/server/database/app.js @@ -1,42 +1,49 @@ +// server/database/app.js + const express = require('express'); const mongoose = require('mongoose'); const fs = require('fs'); -const cors = require('cors') -const app = express() +const cors = require('cors'); +const app = express(); const port = 3030; -app.use(cors()) +app.use(cors()); app.use(require('body-parser').urlencoded({ extended: false })); +// Load JSON files (make sure you’re running from the same folder) const reviews_data = JSON.parse(fs.readFileSync("reviews.json", 'utf8')); const dealerships_data = JSON.parse(fs.readFileSync("dealerships.json", 'utf8')); -mongoose.connect("mongodb://mongo_db:27017/",{'dbName':'dealershipsDB'}); - +// Connect to MongoDB (dbName: dealershipsDB) +mongoose.connect( + "mongodb://mongo_db:27017/", + { dbName: 'dealershipsDB', useNewUrlParser: true, useUnifiedTopology: true } +); +// Import the Mongoose models const Reviews = require('./review'); - const Dealerships = require('./dealership'); +// +// Seed or overwrite existing collections on startup +// try { - Reviews.deleteMany({}).then(()=>{ + Reviews.deleteMany({}).then(() => { Reviews.insertMany(reviews_data['reviews']); }); - Dealerships.deleteMany({}).then(()=>{ + Dealerships.deleteMany({}).then(() => { Dealerships.insertMany(dealerships_data['dealerships']); }); - } catch (error) { - res.status(500).json({ error: 'Error fetching documents' }); + console.error("Error seeding data:", error); } - -// Express route to home +// === 1) Home route === app.get('/', async (req, res) => { - res.send("Welcome to the Mongoose API") + res.send("Welcome to the Mongoose API"); }); -// Express route to fetch all reviews +// === 2) Fetch all reviews === app.get('/fetchReviews', async (req, res) => { try { const documents = await Reviews.find(); @@ -46,59 +53,86 @@ app.get('/fetchReviews', async (req, res) => { } }); -// Express route to fetch reviews by a particular dealer +// === 3) Fetch reviews for a particular dealer === app.get('/fetchReviews/dealer/:id', async (req, res) => { try { - const documents = await Reviews.find({dealership: req.params.id}); + const documents = await Reviews.find({ dealership: req.params.id }); res.json(documents); } catch (error) { res.status(500).json({ error: 'Error fetching documents' }); } }); -// Express route to fetch all dealerships +// +// === 4) Fetch all dealerships === +// app.get('/fetchDealers', async (req, res) => { -//Write your code here + try { + const dealers = await Dealerships.find(); + res.json(dealers); + } catch (error) { + res.status(500).json({ error: 'Error fetching dealerships' }); + } }); -// Express route to fetch Dealers by a particular state +// +// === 5) Fetch dealerships in a particular state === +// app.get('/fetchDealers/:state', async (req, res) => { -//Write your code here + try { + const stateParam = req.params.state; + const dealers = await Dealerships.find({ state: stateParam }); + res.json(dealers); + } catch (error) { + res.status(500).json({ error: 'Error fetching dealerships by state' }); + } }); -// Express route to fetch dealer by a particular id +// +// === 6) Fetch a single dealer by ID === +// app.get('/fetchDealer/:id', async (req, res) => { -//Write your code here + try { + const dealerId = req.params.id; + const dealer = await Dealerships.findOne({ id: dealerId }); + if (dealer) { + res.json(dealer); + } else { + res.status(404).json({ message: 'Dealer not found' }); + } + } catch (error) { + res.status(500).json({ error: 'Error fetching dealer by id' }); + } }); -//Express route to insert review +// +// === 7) Insert a new review === app.post('/insert_review', express.raw({ type: '*/*' }), async (req, res) => { - data = JSON.parse(req.body); - const documents = await Reviews.find().sort( { id: -1 } ) - let new_id = documents[0]['id']+1 - - const review = new Reviews({ - "id": new_id, - "name": data['name'], - "dealership": data['dealership'], - "review": data['review'], - "purchase": data['purchase'], - "purchase_date": data['purchase_date'], - "car_make": data['car_make'], - "car_model": data['car_model'], - "car_year": data['car_year'], - }); - + const data = JSON.parse(req.body); try { + const lastReview = await Reviews.find().sort({ id: -1 }).limit(1); + const newId = (lastReview[0]?.id || 0) + 1; + + const review = new Reviews({ + id: newId, + name: data['name'], + dealership: data['dealership'], + review: data['review'], + purchase: data['purchase'], + purchase_date: data['purchase_date'], + car_make: data['car_make'], + car_model: data['car_model'], + car_year: data['car_year'], + }); const savedReview = await review.save(); res.json(savedReview); } catch (error) { - console.log(error); + console.error(error); res.status(500).json({ error: 'Error inserting review' }); } }); -// Start the Express server +// === Start the Express server === app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); diff --git a/server/database/database/README.md b/server/database/database/README.md new file mode 100644 index 0000000000..36dec0639c --- /dev/null +++ b/server/database/database/README.md @@ -0,0 +1,24 @@ +# OpenShift Clients + +The OpenShift client `oc` simplifies working with Kubernetes and OpenShift +clusters, offering a number of advantages over `kubectl` such as easy login, +kube config file management, and access to developer tools. The `kubectl` +binary is included alongside for when strict Kubernetes compliance is necessary. + +To learn more about OpenShift, visit [docs.openshift.com](https://docs.openshift.com) +and select the version of OpenShift you are using. + +## Installing the tools + +After extracting this archive, move the `oc` and `kubectl` binaries +to a location on your PATH such as `/usr/local/bin`. Then run: + + oc login [API_URL] + +to start a session against an OpenShift cluster. After login, run `oc` and +`oc help` to learn more about how to get started with OpenShift. + +## License + +OpenShift is licensed under the Apache Public License 2.0. The source code for this +program is [located on github](https://github.com/openshift/oc). diff --git a/server/database/database/bin/2to3-3.8 b/server/database/database/bin/2to3-3.8 new file mode 100755 index 0000000000..4d1b3b8508 --- /dev/null +++ b/server/database/database/bin/2to3-3.8 @@ -0,0 +1,5 @@ +#! /usr/bin/python3.8 +import sys +from lib2to3.main import main + +sys.exit(main("lib2to3.fixes")) diff --git a/server/database/database/bin/FileCheck-17 b/server/database/database/bin/FileCheck-17 new file mode 100755 index 0000000000..bbbce270de Binary files /dev/null and b/server/database/database/bin/FileCheck-17 differ diff --git a/server/database/database/bin/GET b/server/database/database/bin/GET new file mode 100755 index 0000000000..355ff3dae9 --- /dev/null +++ b/server/database/database/bin/GET @@ -0,0 +1,561 @@ +#!/usr/bin/perl + +# Simple user agent using LWP library. + +=head1 NAME + +lwp-request - Simple command line user agent + +=head1 SYNOPSIS + +B [B<-afPuUsSedvhx>] [B<-m> I] [B<-b> I] [B<-t> I] + [B<-i> I] [B<-c> I] + [B<-C> I] [B<-p> I] [B<-o> I] I... + +=head1 DESCRIPTION + +This program can be used to send requests to WWW servers and your +local file system. The request content for POST and PUT +methods is read from stdin. The content of the response is printed on +stdout. Error messages are printed on stderr. The program returns a +status value indicating the number of URLs that failed. + +The options are: + +=over 4 + +=item -m + +Set which method to use for the request. If this option is not used, +then the method is derived from the name of the program. + +=item -f + +Force request through, even if the program believes that the method is +illegal. The server might reject the request eventually. + +=item -b + +This URI will be used as the base URI for resolving all relative URIs +given as argument. + +=item -t + +Set the timeout value for the requests. The timeout is the amount of +time that the program will wait for a response from the remote server +before it fails. The default unit for the timeout value is seconds. +You might append "m" or "h" to the timeout value to make it minutes or +hours, respectively. The default timeout is '3m', i.e. 3 minutes. + +=item -i