init webseite ts and claude
This commit is contained in:
8
webseite-ts/backend/.vscode/settings.json
vendored
Normal file
8
webseite-ts/backend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"titleBar.activeForeground": "#333",
|
||||
"titleBar.activeBackground": "#d0d06b",
|
||||
"titleBar.inactiveForeground": "#ddd",
|
||||
"titleBar.inactiveBackground": "#a4a464"
|
||||
}
|
||||
}
|
||||
68
webseite-ts/backend/api.rest
Normal file
68
webseite-ts/backend/api.rest
Normal file
@@ -0,0 +1,68 @@
|
||||
# BROWSE Products
|
||||
|
||||
GET http://127.0.0.1:8000/api/products
|
||||
|
||||
###
|
||||
|
||||
# READ Product
|
||||
|
||||
GET http://127.0.0.1:8000/api/products/c38913d4-9524-461f-85d2-525241166e8e
|
||||
|
||||
|
||||
###
|
||||
|
||||
# ADD Product
|
||||
|
||||
POST http://127.0.0.1:8000/api/products
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Testproduct",
|
||||
"price": 9.99
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# UPDATE Product
|
||||
|
||||
PUT http://127.0.0.1:8000/api/products
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"_id": "940c316b-518c-4a18-b1f6-328e010eef4d",
|
||||
"name": "Testproduct Updated",
|
||||
"price": 123.22
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# DELETE Product
|
||||
|
||||
DELETE http://127.0.0.1:8000/api/products/6cbe4783-cfb5-4ed7-8196-9d6b55217de0
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
# RESET Products
|
||||
PATCH http://127.0.0.1:8000/api/products/reset
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"reset": true
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# SAVE Products
|
||||
PATCH http://127.0.0.1:8000/api/products/save
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"save": true
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
38
webseite-ts/backend/data/products.bakup.json
Normal file
38
webseite-ts/backend/data/products.bakup.json
Normal file
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"_id": "6cbe4783-cfb5-4ed7-8196-9d6b55217de0",
|
||||
"position": 1,
|
||||
"name": "3Doodler 3D Printing Pen",
|
||||
"price": 29.99
|
||||
},
|
||||
{
|
||||
"_id": "c38913d4-9524-461f-85d2-525241166e8e",
|
||||
"position": 2,
|
||||
"name": "Powerstation 5- E. Maximus Chargus",
|
||||
"price": 44.95
|
||||
},
|
||||
{
|
||||
"_id": "cdc887bd-fb0e-4485-8516-0a3720c3bb72",
|
||||
"position": 3,
|
||||
"name": "8-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 6.99
|
||||
},
|
||||
{
|
||||
"_id": "bded2710-8db8-41a5-87af-269195eab449",
|
||||
"position": 4,
|
||||
"name": "16-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"_id": "3b3352c1-789d-4b4f-8a93-f05f44ece5a5",
|
||||
"position": 5,
|
||||
"name": "32-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 18.99
|
||||
},
|
||||
{
|
||||
"_id": "5af39d33-cbcd-42c7-a160-50084faa91ba",
|
||||
"position": 6,
|
||||
"name": "64-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 21.99
|
||||
}
|
||||
]
|
||||
38
webseite-ts/backend/data/products.json
Normal file
38
webseite-ts/backend/data/products.json
Normal file
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"_id": "6cbe4783-cfb5-4ed7-8196-9d6b55217de0",
|
||||
"position": 1,
|
||||
"name": "3Doodler 3D Printing Pen",
|
||||
"price": 29.99
|
||||
},
|
||||
{
|
||||
"_id": "c38913d4-9524-461f-85d2-525241166e8e",
|
||||
"position": 2,
|
||||
"name": "Powerstation 5- E. Maximus Chargus",
|
||||
"price": 44.95
|
||||
},
|
||||
{
|
||||
"_id": "cdc887bd-fb0e-4485-8516-0a3720c3bb72",
|
||||
"position": 3,
|
||||
"name": "8-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 6.99
|
||||
},
|
||||
{
|
||||
"_id": "bded2710-8db8-41a5-87af-269195eab449",
|
||||
"position": 4,
|
||||
"name": "16-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"_id": "3b3352c1-789d-4b4f-8a93-f05f44ece5a5",
|
||||
"position": 5,
|
||||
"name": "32-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 18.99
|
||||
},
|
||||
{
|
||||
"_id": "5af39d33-cbcd-42c7-a160-50084faa91ba",
|
||||
"position": 6,
|
||||
"name": "64-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 21.99
|
||||
}
|
||||
]
|
||||
113
webseite-ts/backend/model/ProductModel.js
Normal file
113
webseite-ts/backend/model/ProductModel.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import fs from 'node:fs';
|
||||
import { v4 as id } from 'uuid';
|
||||
|
||||
// Factoryfunction
|
||||
const ProductModel = () => {
|
||||
let products = [];
|
||||
|
||||
const load = () => {
|
||||
try {
|
||||
products = JSON.parse(fs.readFileSync('./data/products.json', 'utf-8'));
|
||||
} catch (error) {
|
||||
console.log('Something went wrong: ', error);
|
||||
products = [];
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
try {
|
||||
products = JSON.parse(fs.readFileSync('./data/products.bakup.json', 'utf-8'));
|
||||
} catch (error) {
|
||||
console.log('Something went wrong: ', error);
|
||||
products = [];
|
||||
}
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
try {
|
||||
fs.writeFileSync('./data/products.json', JSON.stringify(products, null, 2), 'utf-8');
|
||||
} catch (error) {
|
||||
console.log('Something went wrong:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const getAll = () => {
|
||||
return products;
|
||||
};
|
||||
|
||||
const get = (id) => {
|
||||
const product = products.find((product) => {
|
||||
return product._id === id;
|
||||
});
|
||||
|
||||
// Guard
|
||||
if (!product) return null;
|
||||
|
||||
return product;
|
||||
};
|
||||
|
||||
const add = (product) => {
|
||||
const { name, price } = product;
|
||||
|
||||
if (!hasAllProps(product)) return false;
|
||||
|
||||
products.push({
|
||||
_id: id(),
|
||||
name,
|
||||
price: Number(price.toFixed(2)),
|
||||
position: products.length + 1,
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const update = (product) => {
|
||||
const foundIdx = products.findIndex((obj) => {
|
||||
return obj._id === product._id;
|
||||
});
|
||||
|
||||
// Guard
|
||||
if (foundIdx === -1 || !hasAllProps(product)) return false;
|
||||
|
||||
products[foundIdx] = product;
|
||||
return true;
|
||||
};
|
||||
|
||||
const remove = (id) => {
|
||||
const foundIdx = products.findIndex((obj) => {
|
||||
return obj._id === id;
|
||||
});
|
||||
|
||||
// Guard
|
||||
if (foundIdx === -1) return false;
|
||||
|
||||
products.splice(foundIdx, 1);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* hasAllProps - Überprüfung ob Product "name" und "price"
|
||||
* @return boolean
|
||||
**/
|
||||
const hasAllProps = (product) => {
|
||||
// return Object.hasOwn(product, 'name') && Object.hasOwn(product, 'price');
|
||||
|
||||
const keys = Object.keys(product); // => ['name', 'price']
|
||||
return keys.includes('name') && keys.includes('price');
|
||||
};
|
||||
|
||||
return {
|
||||
// load: load
|
||||
load,
|
||||
save,
|
||||
add,
|
||||
getAll,
|
||||
get,
|
||||
update,
|
||||
delete: remove,
|
||||
reset,
|
||||
};
|
||||
};
|
||||
|
||||
export default ProductModel;
|
||||
20
webseite-ts/backend/package.json
Normal file
20
webseite-ts/backend/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "website-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "npx nodemon server.js",
|
||||
"server": "npx nodemon server.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"type": "module",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.2.1",
|
||||
"uuid": "^14.0.1"
|
||||
}
|
||||
}
|
||||
158
webseite-ts/backend/server.js
Normal file
158
webseite-ts/backend/server.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import express from 'express';
|
||||
import color from 'chalk';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config(); // Sensible Konfigurationsvariablen in .env Datei auslagern
|
||||
|
||||
// Model
|
||||
import ProductModel from './model/ProductModel.js'; // Dateiendung nicht vergessen
|
||||
|
||||
const PORT = process.env.SERVER_PORT; // || 8000;
|
||||
const HOST = process.env.SERVER_HOST; // || '127.0.0.1'; // 'localhost'
|
||||
const BASE_URL = `http://${HOST}:${PORT}`;
|
||||
|
||||
const app = express();
|
||||
const products = ProductModel();
|
||||
|
||||
const corsOptions = {
|
||||
origin: ['http://localhost:3000', 'http://127.0.0.1:3000'],
|
||||
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
|
||||
};
|
||||
|
||||
// lade Produkte
|
||||
products.load();
|
||||
|
||||
// Middleware
|
||||
app.use(cors(corsOptions)); // Fängt alle HTTP-Requests vor den Routenabfragen ab.
|
||||
app.use(express.json()); // HTTP-Request Body wird als JSON Objekt erwartet und geparsed wird. // app.use(bodyParser.json()) - Modul body-parser war in express 4 notwendig
|
||||
|
||||
app.use((req, res, next) => {
|
||||
console.log(color.yellow('HTTP-Method: '), color.magenta(req.method));
|
||||
next();
|
||||
});
|
||||
|
||||
// Routes
|
||||
app.get('/', (req, res) => {
|
||||
// console.log(req.headers);
|
||||
|
||||
res.send('SERVER WORKS!'); // HTTP-Response wird gesendet.
|
||||
});
|
||||
|
||||
app.get('/persons', (req, res) => {
|
||||
// res.setHeader('Content-Type', 'application/json'); // nicht notwendig den Content-Type selber zu definieren, da das express automatisch zuweist.
|
||||
|
||||
const persons = [
|
||||
{ firstName: 'John', lastName: 'Wick' },
|
||||
{ firstName: 'Jane', lastName: 'Doe' },
|
||||
{ firstName: 'Max', lastName: 'Mustermann' },
|
||||
];
|
||||
// res.send(JSON.stringify(persons)); // nicht notwendig JS Objekte in JSON Strings umzuwandeln, das das express automatisch umwandelt
|
||||
|
||||
res.send(persons);
|
||||
});
|
||||
|
||||
// REST API ======
|
||||
// Representational State Transfer (abgekürzt REST)
|
||||
// GET - Daten Holen
|
||||
// POST - Daten hinzufügen
|
||||
// PUT/PATCH - Daten aktualisieren / Einen Datensatz aktualisieren
|
||||
// DELETE - Daten löschen
|
||||
|
||||
// HTTP - Methode - POST
|
||||
// (C)RUD - create - neues Produkt erstellen
|
||||
// BRE(A)D - add
|
||||
app.post('/api/products', (req, res) => {
|
||||
const product = req.body;
|
||||
|
||||
if (!products.add(product)) {
|
||||
return res.status(400).send({ msg: 'Could not add product', status: 400, success: false });
|
||||
}
|
||||
|
||||
return res.send({ msg: 'Product added', status: 200, success: true, data: product });
|
||||
});
|
||||
|
||||
// HTTP - Methode - GET
|
||||
// C(R)UD - read
|
||||
// (B)READ - browse
|
||||
app.get('/api/products', (req, res) => {
|
||||
const data = products.getAll();
|
||||
return res.send(data);
|
||||
});
|
||||
|
||||
// HTTP - Methode - GET
|
||||
// C(R)UD - read
|
||||
// B(R)EAD - read
|
||||
app.get('/api/products/:id', (req, res) => {
|
||||
const id = req.params.id; // routenparameter :id wird als Eigenschaft in params-Objekt abgelegt
|
||||
const product = products.get(id);
|
||||
|
||||
if (!product) {
|
||||
return res.status(400).send({ msg: 'Could not get product', status: 400, success: false });
|
||||
}
|
||||
|
||||
return res.send(product);
|
||||
});
|
||||
|
||||
// HTTP - Methode - PUT/PATCH
|
||||
// CR(U)D - update
|
||||
// BR(E)AD - edit
|
||||
app.put('/api/products', (req, res) => {
|
||||
const product = req.body;
|
||||
|
||||
const updated = products.update(product);
|
||||
|
||||
if (!updated) {
|
||||
return res.status(400).send({ msg: 'Could not update product', status: 400, success: false });
|
||||
}
|
||||
|
||||
return res.send({ msg: 'Product updated', status: 200, success: true, data: product });
|
||||
});
|
||||
|
||||
// HTTP - Methode - PATCH
|
||||
// reseten von Produkten
|
||||
app.patch('/api/products/reset', (req, res) => {
|
||||
const reset = req.body.reset;
|
||||
|
||||
if (!reset) {
|
||||
return res.status(400).send({ msg: 'Could not reset products', status: 400, success: false });
|
||||
}
|
||||
|
||||
products.reset();
|
||||
|
||||
return res.send({ msg: 'Products reseted', status: 200, success: true, data: reset });
|
||||
});
|
||||
|
||||
// HTTP - Methode - PATCH
|
||||
// speichern von Produkten
|
||||
app.patch('/api/products/save', (req, res) => {
|
||||
const save = req.body.save;
|
||||
|
||||
if (!save) {
|
||||
return res.status(400).send({ msg: 'Could not save products', status: 400, success: false });
|
||||
}
|
||||
|
||||
products.save();
|
||||
|
||||
return res.send({ msg: 'Products saved', status: 200, success: true, data: save });
|
||||
});
|
||||
|
||||
// HTTP - Methode - DELETE
|
||||
// CRU(D) - delete
|
||||
// BREA(D) - delete
|
||||
app.delete('/api/products/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
if (!products.delete(id)) {
|
||||
return res.status(400).send({ msg: 'Could not delete product', status: 400, success: false });
|
||||
}
|
||||
|
||||
return res.send({ msg: 'Product deleted', status: 200, success: true, data: id });
|
||||
});
|
||||
|
||||
// =========
|
||||
|
||||
app.listen(PORT, HOST, () => {
|
||||
console.log(color.magenta(`🚀 Server is running at: ${BASE_URL}`));
|
||||
console.log(color.yellow('CTRL + C to close.'));
|
||||
});
|
||||
19
webseite-ts/backend/utils/json-modifier.js
Normal file
19
webseite-ts/backend/utils/json-modifier.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import fs from 'node:fs';
|
||||
import { v4 as id } from 'uuid';
|
||||
|
||||
console.log(id()); // 97412ca6-c7a8-4868-a49b-3cf4c2258dd8
|
||||
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync('../data/products.json', 'utf-8'));
|
||||
|
||||
const products = data.map((product, index) => {
|
||||
return { _id: product._id || id(), position: index + 1, ...product };
|
||||
});
|
||||
|
||||
console.log(products);
|
||||
|
||||
fs.writeFileSync('../data/products.json', JSON.stringify(products, null, 2), 'utf-8');
|
||||
console.log('File modified');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
28
webseite-ts/backend/utils/package-lock.json
generated
Normal file
28
webseite-ts/backend/utils/package-lock.json
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "utils",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "utils",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"uuid": "^14.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz",
|
||||
"integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
webseite-ts/backend/utils/package.json
Normal file
14
webseite-ts/backend/utils/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "utils",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "json-modifier.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"uuid": "^14.0.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user