Compare commits

...

4 Commits

Author SHA1 Message Date
45e07d6413 Increase products after advance date 2025-12-31 17:59:08 +03:00
Artur
c6604703fe Merge pull request #1 from Tsettaro/codex/check-server-side-code
Fix DB queries and errors, use configured PORT and graceful shutdown, restore Random_Increase
2025-12-31 15:14:32 +03:00
Artur
d39f5f129e Restore Random_Increase helper 2025-12-31 15:12:47 +03:00
a1d326aa40 Больше api endpoint-ов 2025-12-31 14:50:29 +03:00
2 changed files with 358 additions and 18 deletions

View File

@@ -5,10 +5,14 @@ export const DB_USER_ERROR = 'USER';
let currentDate = (new Date()).toISOString().slice(0, 10);
export async function getCurrentDate() {
export function getCurrentDate() {
return currentDate;
}
function Random_Increase() {
return Math.floor(Math.random() * 5) + 1;
}
export async function setCurrentDate(newDate) {
currentDate = newDate;
console.log(`📅 Date changed to: ${currentDate}`);
@@ -62,6 +66,24 @@ export default class DBAdapter {
console.log("DISCONNECT");
}
async stock(){
try {
const products = await this.#dbClient.query('SELECT id FROM products');
for (const product of products) {
const increment = Random_Increase();
await this.#dbClient.query('UPDATE products SET quantity = quantity + ? WHERE id = ?', [
increment,
product.id
]);
}
} catch (err){
return Promise.reject({
type: err
}
);
}
}
async getProducts() {
try {
const products = await this.#dbClient.query('SELECT * FROM products ORDER BY name');
@@ -144,7 +166,7 @@ export default class DBAdapter {
}
}
async deleteOrdersByIds(orderIds) {
async clearOrders(orderIds) {
if (!orderIds || orderIds.length === 0) {
console.log("No orders to delete");
return;
@@ -165,4 +187,215 @@ export default class DBAdapter {
return Promise.reject();
}
}
}
async changeOrderInfo({ id, customer_name, orderDate }){
if (!id) {
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Order ID is required")
});
}
const currentDate = await getCurrentDate();
if (orderDate < currentDate) {
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Order date cannot be less than current date")
});
}
try {
const order = await this.#dbClient.query('SELECT * FROM orders WHERE id = ?', [id]);
if (!order){
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Order not found")
});
}
const updatedName = customer_name;
const updatedDate = orderDate;
await this.#dbClient.query('UPDATE orders SET customer_name = ?, order_date = ? WHERE id = ?',
[updatedName, updatedDate, id]
);
} catch(err){
return Promise.reject({
type: DB_INTERNAL_ERROR,
error: new Error("Server error")
});
}
}
async deleteOrderById(order_id){
try {
const orderItems = await this.#dbClient.query(
`SELECT product_id, quantity FROM order_items WHERE order_id = ?`,
[order_id]
);
for (const item of orderItems){
await this.#dbClient.query('UPDATE products SET quantity = quantity + ? WHERE id = ?',
[item.quantity, item.product_id]
);
}
await this.#dbClient.query(
`DELETE FROM orders WHERE id = ?`,
[order_id]
);
} catch(err){
return Promise.reject({
type: DB_INTERNAL_ERROR,
error: new Error("Server error")
});
}
}
async addOrderItem({ orderId, productId, quantity }){
await this.#dbClient.query('BEGIN');
try {
const product = await this.#dbClient.query(
'SELECT quantity FROM products WHERE id = ?', [productId]
);
const productRow = product?.[0];
if (!productRow || productRow.quantity < quantity){
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Insufficient product quantity")
});
}
const result = await this.#dbClient.query(
'INSERT INTO order_items (order_id, product_id, quantity) VALUES (?, ?, ?)',
[orderId, productId, quantity]
);
await this.#dbClient.query(
'UPDATE products SET quantity = quantity - ? WHERE id = ?',
[quantity, productId]
);
await this.#dbClient.query('COMMIT');
} catch (err){
await this.#dbClient.query('ROLLBACK');
return Promise.reject();
}
}
async updateOrderItem({ itemId, quantity }){
await this.#dbClient.query('BEGIN');
try {
const item = await this.#dbClient.query(
'SELECT product_id, quantity FROM order_items WHERE id = ?', [itemId]
);
const itemRow = item?.[0];
if (!itemRow){
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Order item not found")
});
}
const diff = quantity - itemRow.quantity;
if (diff > 0){
const product = await this.#dbClient.query(
'SELECT quantity FROM products WHERE id = ?', [itemRow.product_id]
);
const productRow = product?.[0];
if (!productRow || productRow.quantity < diff){
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Insufficient product quantity")
});
}
await this.#dbClient.query(
'UPDATE products SET quantity = quantity - ? WHERE id = ?',
[diff, itemRow.product_id]
);
}
if (diff < 0) {
await this.#dbClient.query(
'UPDATE products SET quantity = quantity + ? WHERE id = ?',
[-diff, itemRow.product_id]
);
}
await this.#dbClient.query(
'UPDATE order_items SET quantity = ? WHERE id = ?',
[quantity, itemId]
);
await this.#dbClient.query('COMMIT');
} catch (err){
await this.#dbClient.query('ROLLBACK');
return Promise.reject();
}
}
async deleteOrderItem(itemId){
await this.#dbClient.query('BEGIN');
try {
const item = await this.#dbClient.query(
'SELECT product_id, quantity FROM order_items WHERE id = ?', [itemId]
);
const itemRow = item?.[0];
if (!itemRow){
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Order item not found")
});
}
await this.#dbClient.query(
'UPDATE products SET quantity = quantity + ? WHERE id = ?',
[itemRow.quantity, itemRow.product_id]
);
await this.#dbClient.query(
'DELETE FROM order_items WHERE id = ?',
[itemId]
);
await this.#dbClient.query('COMMIT');
} catch (err){
await this.#dbClient.query('ROLLBACK');
return Promise.reject();
}
}
async moveOrderItem( {itemId, targetOrderId }){
await this.#dbClient.query('BEGIN');
try {
const item = await this.#dbClient.query(
'SELECT id, order_id FROM order_items WHERE id = ?', [itemId]
);
const itemRow = item?.[0];
if (!itemRow){
return Promise.reject({
type: DB_USER_ERROR,
error: new Error("Order item not found")
});
}
await this.#dbClient.query(
'UPDATE order_items SET order_id = ? WHERE id = ?',
[targetOrderId, itemId]
);
await this.#dbClient.query('COMMIT');
} catch (err){
await this.#dbClient.query('ROLLBACK');
return Promise.reject();
}
}
}

View File

@@ -16,6 +16,8 @@ const {
PASSWORD
} = process.env;
const appPort = 3000;
const app = express();
app.use(express.json());
@@ -33,12 +35,8 @@ function AddDays(dateString, days) {
return date.toISOString().slice(0, 10);
}
function Random_Increase() {
return Math.floor(Math.random() * 5) + 1;
}
app.get('/api/current-date', async (req, res) => {
let currentDate = await getCurrentDate();
let currentDate = getCurrentDate();
res.json({ currentDate });
});
@@ -68,13 +66,15 @@ app.get('/api/products', async(req, res) => {
});
app.post('/api/current-date/advance', async (req, res) => {
let currentDate = await getCurrentDate();
let currentDate = getCurrentDate();
try {
const raw_ids = await adapter.getOrdersByDate(currentDate);
const ids = raw_ids.map(item => item.id);
await adapter.deleteOrdersByIds(ids);
if (raw_ids && raw_ids.length > 0) {
const ids = raw_ids.map(item => item.id);
await adapter.clearOrders(ids);
}
let nextDate = AddDays(currentDate, 1);
await adapter.stock();
await setCurrentDate(nextDate);
res.json({ currentDate: nextDate });
} catch (err){
@@ -82,7 +82,7 @@ app.post('/api/current-date/advance', async (req, res) => {
res.message = "WHOOPS";
res.json({
timeStamp: new Date().toISOString(),
statusCode: 500,
statusCode: res.statusCode,
error : `${err}`
})
}
@@ -170,15 +170,122 @@ app.post('/api/orders', async (req, res) => {
}
});
app.delete('/api/orders/:id', async (req, res) => {
try {
await adapter.deleteOrderById(req.params.id);
res.statusCode = 200;
res.message = "OK";
res.json({message: "success"});
} catch (err){
res.statusCode = 500;
res.message = "WHOOPS";
res.json({
timeStamp: new Date().toISOString(),
statusCode: 500,
error: `${err}`
});
}
})
app.post('/api/orders/:orderId/items', async (req, res) => {
let orderId = req.params.orderId;
let { productId, quantity } = req.body;
try {
await adapter.addOrderItem({
orderId,
productId,
quantity
});
res.status(201).json({
orderId,
productId,
quantity
});
} catch (err) {
// TODO
res.status(500).json({
timeStamp: new Date().toISOString(),
statusCode: 500,
});
}
});
app.put('/api/orders/:orderId/items/:itemId', async (req, res) => {
let itemId = req.params.itemId;
let quantity = req.body.quantity;
try {
await adapter.updateOrderItem({
itemId,
quantity
});
res.status(201).json({
itemId,
quantity
});
} catch (err) {
// TODO
res.status(500).json({
timeStamp: new Date().toISOString(),
statusCode: 500,
});
}
});
app.delete('/api/orders/:orderId/items/:itemId', async (req, res) => {
try {
await adapter.deleteOrderItem(req.params.itemId);
res.status(204).end();
} catch (err){
res.statusCode = 500;
res.message = "WHOOPS";
res.json({
timeStamp: new Date().toISOString(),
statusCode: 500,
error: `${err}`
});
}
})
app.post('/api/order-items/:itemId/move', async (req, res) => {
let itemId = req.params.itemId;
let targetOrderId = req.body.targetOrderId;
try {
await adapter.moveOrderItem({
itemId,
targetOrderId,
});
res.status(201).json({
itemId,
targetOrderId,
});
} catch (err) {
// TODO
res.status(500).json({
timeStamp: new Date().toISOString(),
statusCode: 500,
});
}
});
app.use((req, res) => {
res.status(404).json({ error: 'Invalid route' });
});
app.listen(3000, async() => {
const server = app.listen(appPort, async() => {
try {
await adapter.connect();
console.log(`✅ Server running on port 3000`);
console.log(`📡 Local: http://localhost:3000`);
console.log(`✅ Server running on port ${appPort}`);
console.log(`📡 Local: http://localhost:${appPort}`);
} catch(err){
console.log("Shutting down application...");
process.exit(100);
@@ -187,8 +294,8 @@ app.listen(3000, async() => {
process.on('SIGTERM', () => {
console.log("CLOSE APP");
app.close( async() => {
server.close(async () => {
await adapter.disconnect();
console.log("DB DISCONNECTED");
});
});
});