move from SQLite to MariaDB
This commit is contained in:
parent
5d30577232
commit
ac492b1301
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
/node_modules/
|
/node_modules/
|
||||||
/users.db
|
|
||||||
cron_update.log
|
cron_update.log
|
||||||
|
.env
|
||||||
|
|||||||
11
db.js
Normal file
11
db.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const mariadb = require("mariadb");
|
||||||
|
|
||||||
|
const pool = mariadb.createPool({
|
||||||
|
host: "10.0.0.1", // "mariadb" if in Docker
|
||||||
|
user: "minecraft_shop_user",
|
||||||
|
password: "UsersBeliev3sNewsp4p3r",
|
||||||
|
database: "minecraft-shop",
|
||||||
|
connectionLimit: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = pool;
|
||||||
65
package-lock.json
generated
65
package-lock.json
generated
@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
"mariadb": "^3.4.5",
|
||||||
"sqlight": "^1.0.0-alpha.8",
|
"sqlight": "^1.0.0-alpha.8",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
@ -98,6 +99,21 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/geojson": {
|
||||||
|
"version": "7946.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||||
|
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz",
|
||||||
|
"integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/shortid": {
|
"node_modules/@types/shortid": {
|
||||||
"version": "0.0.29",
|
"version": "0.0.29",
|
||||||
"resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz",
|
||||||
@ -554,6 +570,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/denque": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@ -1318,6 +1343,40 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mariadb": {
|
||||||
|
"version": "3.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.4.5.tgz",
|
||||||
|
"integrity": "sha512-gThTYkhIS5rRqkVr+Y0cIdzr+GRqJ9sA2Q34e0yzmyhMCwyApf3OKAC1jnF23aSlIOqJuyaUFUcj7O1qZslmmQ==",
|
||||||
|
"license": "LGPL-2.1-or-later",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/geojson": "^7946.0.16",
|
||||||
|
"@types/node": "^24.0.13",
|
||||||
|
"denque": "^2.1.0",
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
|
"lru-cache": "^10.4.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mariadb/node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mariadb/node_modules/lru-cache": {
|
||||||
|
"version": "10.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@ -2368,6 +2427,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unique-filename": {
|
"node_modules/unique-filename": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
"mariadb": "^3.4.5",
|
||||||
"sqlight": "^1.0.0-alpha.8",
|
"sqlight": "^1.0.0-alpha.8",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
|
|||||||
@ -699,7 +699,7 @@ function getCartItemCount(itemId) {
|
|||||||
|
|
||||||
//check if item can be added to cart (if item_stock > cart_item_count)
|
//check if item can be added to cart (if item_stock > cart_item_count)
|
||||||
async function canAddToCart(itemId) {
|
async function canAddToCart(itemId) {
|
||||||
const item_stock = await getItemStock(itemId);
|
const item_stock = (await getItemStock(itemId)) || 0;
|
||||||
const cart_item_count = getCartItemCount(itemId);
|
const cart_item_count = getCartItemCount(itemId);
|
||||||
return item_stock > cart_item_count;
|
return item_stock > cart_item_count;
|
||||||
}
|
}
|
||||||
|
|||||||
35
server.js
35
server.js
@ -2,7 +2,7 @@ const express = require("express");
|
|||||||
const http = require("http");
|
const http = require("http");
|
||||||
const WebSocket = require("ws");
|
const WebSocket = require("ws");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
const pool = require("./db");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3100;
|
const PORT = process.env.PORT || 3100;
|
||||||
@ -44,38 +44,7 @@ function normalizeItems(arr) {
|
|||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const wss = new WebSocket.Server({ server });
|
const wss = new WebSocket.Server({ server });
|
||||||
|
|
||||||
const db = new sqlite3.Database(path.join(__dirname, "users.db"));
|
require("./users-api")(app, wss, pool);
|
||||||
|
|
||||||
// Create table if it doesn't exist
|
|
||||||
db.serialize(() => {
|
|
||||||
db.run(`
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
username TEXT PRIMARY KEY,
|
|
||||||
password TEXT NOT NULL,
|
|
||||||
balance REAL NOT NULL,
|
|
||||||
adresses TEXT NOT NULL
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
|
|
||||||
db.run(`
|
|
||||||
CREATE TABLE IF NOT EXISTS computers (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
linked_user TEXT,
|
|
||||||
FOREIGN KEY(linked_user) REFERENCES users(username)
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
|
|
||||||
db.run(`
|
|
||||||
CREATE TABLE IF NOT EXISTS shop_items (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT UNIQUE NOT NULL,
|
|
||||||
price REAL NOT NULL,
|
|
||||||
enabled INTEGER NOT NULL DEFAULT 1
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
require("./users-api")(app, wss, db);
|
|
||||||
|
|
||||||
// Helper to send JSON to a ws client
|
// Helper to send JSON to a ws client
|
||||||
function sendJSON(ws, obj) {
|
function sendJSON(ws, obj) {
|
||||||
|
|||||||
692
users-api.js
692
users-api.js
@ -1,27 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
users-api.js
|
users-api.js
|
||||||
User management API endpoints for Express with SQLite.
|
User management API endpoints for Express with MariaDB pool.
|
||||||
|
|
||||||
Exports a function that takes an Express app instance and sets up:
|
|
||||||
- POST /api/users Create a new user
|
|
||||||
- GET /api/users/:username Get a user by username
|
|
||||||
- PUT /api/users/:username Update a user's balance and linked computers
|
|
||||||
- GET /api/users List all users
|
|
||||||
|
|
||||||
User schema:
|
|
||||||
- username (string, unique)
|
|
||||||
- balance (float)
|
|
||||||
- linked_computers (array of ints, stored as JSON string)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const path = require("path");
|
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
|
|
||||||
// Use a persistent DB file in the project directory
|
// Helper to parse JSON fields
|
||||||
|
function parseJson(val) {
|
||||||
// Helper to parse linked_computers from DB
|
|
||||||
function parseLinkedComputers(val) {
|
|
||||||
try {
|
try {
|
||||||
const arr = JSON.parse(val);
|
const arr = JSON.parse(val);
|
||||||
return Array.isArray(arr) ? arr : [];
|
return Array.isArray(arr) ? arr : [];
|
||||||
@ -30,60 +16,72 @@ function parseLinkedComputers(val) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export a function to register routes
|
// Export function to register routes
|
||||||
module.exports = function (app, wss, db) {
|
module.exports = function (app, wss, pool) {
|
||||||
// Helper to get balance from DB
|
// Helper to get balance
|
||||||
function getBalance(username) {
|
async function getBalance(username) {
|
||||||
const row = db.get("SELECT balance FROM users WHERE username = ?", [
|
const conn = await pool.getConnection();
|
||||||
username,
|
try {
|
||||||
]);
|
const rows = await conn.query(
|
||||||
return row ? row.balance : 0;
|
"SELECT balance FROM users WHERE username = ?",
|
||||||
|
[username],
|
||||||
|
);
|
||||||
|
return rows[0]?.balance || 0;
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Create user
|
|
||||||
app.post("/api/users", express.json(), (req, res) => {
|
// --- USERS ---
|
||||||
|
|
||||||
|
app.post("/api/users", express.json(), async (req, res) => {
|
||||||
const { username, password, adresses } = req.body;
|
const { username, password, adresses } = req.body;
|
||||||
const balance = 1000;
|
const balance = 1000;
|
||||||
let adressesArr = Array.isArray(adresses)
|
|
||||||
? adresses.filter((a) => typeof a === "string")
|
|
||||||
: [];
|
|
||||||
if (typeof username !== "string" || typeof password !== "string") {
|
if (typeof username !== "string" || typeof password !== "string") {
|
||||||
return res.status(400).json({ ok: false, error: "Invalid input" });
|
return res.status(400).json({ ok: false, error: "Invalid input" });
|
||||||
}
|
}
|
||||||
db.run(
|
const adressesArr = Array.isArray(adresses)
|
||||||
"INSERT INTO users (username, password, balance, adresses) VALUES (?, ?, ?, ?)",
|
? adresses.filter((a) => typeof a === "string")
|
||||||
[username, password, balance, JSON.stringify(adressesArr)],
|
: [];
|
||||||
function (err) {
|
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
const conn = await pool.getConnection();
|
||||||
res.json({ ok: true });
|
try {
|
||||||
},
|
await conn.query(
|
||||||
);
|
"INSERT INTO users (username, password, balance, adresses) VALUES (?, ?, ?, ?)",
|
||||||
|
[username, password, balance, JSON.stringify(adressesArr)],
|
||||||
|
);
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get user by username
|
app.get("/api/users/:username", async (req, res) => {
|
||||||
app.get("/api/users/:username", (req, res) => {
|
const conn = await pool.getConnection();
|
||||||
db.get(
|
try {
|
||||||
"SELECT * FROM users WHERE username = ?",
|
const rows = await conn.query("SELECT * FROM users WHERE username = ?", [
|
||||||
[req.params.username],
|
req.params.username,
|
||||||
(err, row) => {
|
]);
|
||||||
if (err || !row)
|
const user = rows[0];
|
||||||
return res.status(404).json({ ok: false, error: "User not found" });
|
if (!user)
|
||||||
if (row && typeof row.adresses === "string") {
|
return res.status(404).json({ ok: false, error: "User not found" });
|
||||||
try {
|
|
||||||
row.adresses = JSON.parse(row.adresses);
|
user.adresses = parseJson(user.adresses);
|
||||||
} catch {
|
|
||||||
row.adresses = [];
|
res.json({ ok: true, user });
|
||||||
}
|
} catch (err) {
|
||||||
}
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
res.json({ ok: true, user: row });
|
} finally {
|
||||||
},
|
conn.release();
|
||||||
);
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update user balance
|
app.put("/api/users/:username", express.json(), async (req, res) => {
|
||||||
app.put("/api/users/:username", express.json(), (req, res) => {
|
|
||||||
const { balance, adresses } = req.body;
|
const { balance, adresses } = req.body;
|
||||||
let updates = [];
|
const updates = [];
|
||||||
let params = [];
|
const params = [];
|
||||||
if (typeof balance === "number") {
|
if (typeof balance === "number") {
|
||||||
updates.push("balance = ?");
|
updates.push("balance = ?");
|
||||||
params.push(balance);
|
params.push(balance);
|
||||||
@ -94,140 +92,165 @@ module.exports = function (app, wss, db) {
|
|||||||
JSON.stringify(adresses.filter((a) => typeof a === "string")),
|
JSON.stringify(adresses.filter((a) => typeof a === "string")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (updates.length === 0) {
|
if (updates.length === 0)
|
||||||
return res
|
return res
|
||||||
.status(400)
|
.status(400)
|
||||||
.json({ ok: false, error: "No valid fields to update" });
|
.json({ ok: false, error: "No valid fields to update" });
|
||||||
}
|
|
||||||
params.push(req.params.username);
|
params.push(req.params.username);
|
||||||
db.run(
|
const conn = await pool.getConnection();
|
||||||
`UPDATE users SET ${updates.join(", ")} WHERE username = ?`,
|
try {
|
||||||
params,
|
const result = await conn.query(
|
||||||
function (err) {
|
`UPDATE users SET ${updates.join(", ")} WHERE username = ?`,
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
params,
|
||||||
if (this.changes === 0)
|
);
|
||||||
return res.status(404).json({ ok: false, error: "User not found" });
|
if (result.affectedRows === 0)
|
||||||
res.json({ ok: true });
|
return res.status(404).json({ ok: false, error: "User not found" });
|
||||||
},
|
res.json({ ok: true });
|
||||||
);
|
} catch (err) {
|
||||||
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// List all users
|
app.get("/api/users", async (req, res) => {
|
||||||
app.get("/api/users", (req, res) => {
|
const conn = await pool.getConnection();
|
||||||
db.all("SELECT * FROM users", [], (err, rows) => {
|
try {
|
||||||
if (err) return res.status(500).json({ ok: false, error: err.message });
|
const rows = await conn.query("SELECT * FROM users");
|
||||||
rows.forEach((row) => {
|
rows.forEach((row) => {
|
||||||
if (row && typeof row.adresses === "string") {
|
row.adresses = parseJson(row.adresses);
|
||||||
try {
|
|
||||||
row.adresses = JSON.parse(row.adresses);
|
|
||||||
} catch {
|
|
||||||
row.adresses = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
res.json({ ok: true, users: rows });
|
res.json({ ok: true, users: rows });
|
||||||
});
|
} catch (err) {
|
||||||
});
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
// Create a new computer and link to user
|
conn.release();
|
||||||
app.post("/api/computers", express.json(), (req, res) => {
|
|
||||||
const { linked_user } = req.body;
|
|
||||||
if (typeof linked_user !== "string") {
|
|
||||||
return res.status(400).json({ ok: false, error: "Invalid input" });
|
|
||||||
}
|
}
|
||||||
db.run(
|
|
||||||
"INSERT INTO computers (linked_user) VALUES (?)",
|
|
||||||
[linked_user],
|
|
||||||
function (err) {
|
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
|
||||||
res.json({ ok: true, id: this.lastID });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// List all computers, or by user
|
// --- COMPUTERS ---
|
||||||
app.get("/api/computers", (req, res) => {
|
|
||||||
|
app.post("/api/computers", express.json(), async (req, res) => {
|
||||||
|
const { linked_user } = req.body;
|
||||||
|
if (typeof linked_user !== "string")
|
||||||
|
return res.status(400).json({ ok: false, error: "Invalid input" });
|
||||||
|
|
||||||
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
const result = await conn.query(
|
||||||
|
"INSERT INTO computers (linked_user) VALUES (?)",
|
||||||
|
[linked_user],
|
||||||
|
);
|
||||||
|
res.json({ ok: true, id: result.insertId });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/api/computers", async (req, res) => {
|
||||||
const user = req.query.user;
|
const user = req.query.user;
|
||||||
const sql = user
|
const sql = user
|
||||||
? "SELECT * FROM computers WHERE linked_user = ?"
|
? "SELECT * FROM computers WHERE linked_user = ?"
|
||||||
: "SELECT * FROM computers";
|
: "SELECT * FROM computers";
|
||||||
const params = user ? [user] : [];
|
const params = user ? [user] : [];
|
||||||
db.all(sql, params, (err, rows) => {
|
|
||||||
if (err) return res.status(500).json({ ok: false, error: err.message });
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
const rows = await conn.query(sql, params);
|
||||||
res.json({ ok: true, computers: rows });
|
res.json({ ok: true, computers: rows });
|
||||||
});
|
} catch (err) {
|
||||||
});
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
// Update a computer's linked user
|
conn.release();
|
||||||
app.put("/api/computers/:id", express.json(), (req, res) => {
|
|
||||||
const { linked_user } = req.body;
|
|
||||||
if (typeof linked_user !== "string") {
|
|
||||||
return res.status(400).json({ ok: false, error: "Invalid input" });
|
|
||||||
}
|
}
|
||||||
db.run(
|
|
||||||
"UPDATE computers SET linked_user = ? WHERE id = ?",
|
|
||||||
[linked_user, req.params.id],
|
|
||||||
function (err) {
|
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
|
||||||
if (this.changes === 0)
|
|
||||||
return res
|
|
||||||
.status(404)
|
|
||||||
.json({ ok: false, error: "Computer not found" });
|
|
||||||
res.json({ ok: true });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get all computers linked to a user
|
app.put("/api/computers/:id", express.json(), async (req, res) => {
|
||||||
app.get("/api/users/:username/computers", (req, res) => {
|
const { linked_user } = req.body;
|
||||||
db.all(
|
if (typeof linked_user !== "string")
|
||||||
"SELECT * FROM computers WHERE linked_user = ?",
|
return res.status(400).json({ ok: false, error: "Invalid input" });
|
||||||
[req.params.username],
|
|
||||||
(err, rows) => {
|
const conn = await pool.getConnection();
|
||||||
if (err) return res.status(500).json({ ok: false, error: err.message });
|
try {
|
||||||
res.json({ ok: true, computers: rows });
|
const result = await conn.query(
|
||||||
},
|
"UPDATE computers SET linked_user = ? WHERE id = ?",
|
||||||
);
|
[linked_user, req.params.id],
|
||||||
|
);
|
||||||
|
if (result.affectedRows === 0)
|
||||||
|
return res.status(404).json({ ok: false, error: "Computer not found" });
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get the user linked to a computer
|
app.get("/api/users/:username/computers", async (req, res) => {
|
||||||
app.get("/api/computers/:id/user", (req, res) => {
|
const conn = await pool.getConnection();
|
||||||
db.get(
|
try {
|
||||||
"SELECT linked_user FROM computers WHERE id = ?",
|
const rows = await conn.query(
|
||||||
[req.params.id],
|
"SELECT * FROM computers WHERE linked_user = ?",
|
||||||
(err, row) => {
|
[req.params.username],
|
||||||
if (err || !row)
|
);
|
||||||
return res
|
res.json({ ok: true, computers: rows });
|
||||||
.status(404)
|
} catch (err) {
|
||||||
.json({ ok: false, error: "Computer not found" });
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
res.json({ ok: true, user: row.linked_user });
|
} finally {
|
||||||
},
|
conn.release();
|
||||||
);
|
}
|
||||||
});
|
|
||||||
// --- SHOP ITEMS API ---
|
|
||||||
|
|
||||||
// Get all enabled items (for homepage)
|
|
||||||
app.get("/api/shop/items", (req, res) => {
|
|
||||||
db.all(
|
|
||||||
"SELECT id, name, price, quantity FROM shop_items WHERE enabled = 1",
|
|
||||||
[],
|
|
||||||
(err, rows) => {
|
|
||||||
if (err) return res.status(500).json({ ok: false, error: err.message });
|
|
||||||
res.json({ ok: true, items: rows });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get all items (admin)
|
app.get("/api/computers/:id/user", async (req, res) => {
|
||||||
app.get("/api/shop/items/all", (req, res) => {
|
const conn = await pool.getConnection();
|
||||||
db.all("SELECT * FROM shop_items", [], (err, rows) => {
|
try {
|
||||||
if (err) return res.status(500).json({ ok: false, error: err.message });
|
const rows = await conn.query(
|
||||||
|
"SELECT linked_user FROM computers WHERE id = ?",
|
||||||
|
[req.params.id],
|
||||||
|
);
|
||||||
|
const user = rows[0]?.linked_user;
|
||||||
|
if (!user)
|
||||||
|
return res.status(404).json({ ok: false, error: "Computer not found" });
|
||||||
|
res.json({ ok: true, user });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- SHOP ITEMS ---
|
||||||
|
|
||||||
|
app.get("/api/shop/items", async (req, res) => {
|
||||||
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
const rows = await conn.query(
|
||||||
|
"SELECT id, name, price, quantity FROM shop_items WHERE enabled = 1",
|
||||||
|
);
|
||||||
res.json({ ok: true, items: rows });
|
res.json({ ok: true, items: rows });
|
||||||
});
|
} catch (err) {
|
||||||
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add a new item (admin)
|
app.get("/api/shop/items/all", async (req, res) => {
|
||||||
app.post("/api/shop/items", express.json(), (req, res) => {
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
const rows = await conn.query("SELECT * FROM shop_items");
|
||||||
|
res.json({ ok: true, items: rows });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/api/shop/items", express.json(), async (req, res) => {
|
||||||
const { name, price, enabled, quantity } = req.body;
|
const { name, price, enabled, quantity } = req.body;
|
||||||
if (
|
if (
|
||||||
typeof name !== "string" ||
|
typeof name !== "string" ||
|
||||||
@ -236,18 +259,21 @@ module.exports = function (app, wss, db) {
|
|||||||
) {
|
) {
|
||||||
return res.status(400).json({ ok: false, error: "Invalid input" });
|
return res.status(400).json({ ok: false, error: "Invalid input" });
|
||||||
}
|
}
|
||||||
db.run(
|
const conn = await pool.getConnection();
|
||||||
"INSERT INTO shop_items (name, price, enabled, quantity) VALUES (?, ?, ?, ?)",
|
try {
|
||||||
[name, price, enabled === 0 ? 0 : 1, quantity],
|
const result = await conn.query(
|
||||||
function (err) {
|
"INSERT INTO shop_items (name, price, enabled, quantity) VALUES (?, ?, ?, ?)",
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
[name, price, enabled ? 1 : 0, quantity],
|
||||||
res.json({ ok: true, id: this.lastID });
|
);
|
||||||
},
|
res.json({ ok: true, id: result.insertId });
|
||||||
);
|
} catch (err) {
|
||||||
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update an item (admin)
|
app.put("/api/shop/items/:id", express.json(), async (req, res) => {
|
||||||
app.put("/api/shop/items/:id", express.json(), (req, res) => {
|
|
||||||
const { name, price, enabled, quantity } = req.body;
|
const { name, price, enabled, quantity } = req.body;
|
||||||
const updates = [];
|
const updates = [];
|
||||||
const params = [];
|
const params = [];
|
||||||
@ -267,123 +293,108 @@ module.exports = function (app, wss, db) {
|
|||||||
updates.push("quantity = ?");
|
updates.push("quantity = ?");
|
||||||
params.push(quantity);
|
params.push(quantity);
|
||||||
}
|
}
|
||||||
if (updates.length === 0) {
|
if (updates.length === 0)
|
||||||
return res
|
return res
|
||||||
.status(400)
|
.status(400)
|
||||||
.json({ ok: false, error: "No valid fields to update" });
|
.json({ ok: false, error: "No valid fields to update" });
|
||||||
}
|
|
||||||
params.push(req.params.id);
|
params.push(req.params.id);
|
||||||
db.run(
|
|
||||||
`UPDATE shop_items SET ${updates.join(", ")} WHERE id = ?`,
|
const conn = await pool.getConnection();
|
||||||
params,
|
try {
|
||||||
function (err) {
|
const result = await conn.query(
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
`UPDATE shop_items SET ${updates.join(", ")} WHERE id = ?`,
|
||||||
if (this.changes === 0)
|
params,
|
||||||
return res.status(404).json({ ok: false, error: "Item not found" });
|
);
|
||||||
res.json({ ok: true });
|
if (result.affectedRows === 0)
|
||||||
},
|
return res.status(404).json({ ok: false, error: "Item not found" });
|
||||||
);
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete an item (admin)
|
app.delete("/api/shop/items/:id", async (req, res) => {
|
||||||
app.delete("/api/shop/items/:id", (req, res) => {
|
const conn = await pool.getConnection();
|
||||||
db.run(
|
try {
|
||||||
"DELETE FROM shop_items WHERE id = ?",
|
const result = await conn.query("DELETE FROM shop_items WHERE id = ?", [
|
||||||
[req.params.id],
|
req.params.id,
|
||||||
function (err) {
|
]);
|
||||||
if (err) return res.status(400).json({ ok: false, error: err.message });
|
if (result.affectedRows === 0)
|
||||||
if (this.changes === 0)
|
return res.status(404).json({ ok: false, error: "Item not found" });
|
||||||
return res.status(404).json({ ok: false, error: "Item not found" });
|
res.json({ ok: true });
|
||||||
res.json({ ok: true });
|
} catch (err) {
|
||||||
},
|
res.status(400).json({ ok: false, error: err.message });
|
||||||
);
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- SHOP BUY & USER TRANSFER ---
|
||||||
|
|
||||||
app.post("/api/shop/items/buy", async (req, res) => {
|
app.post("/api/shop/items/buy", async (req, res) => {
|
||||||
const { userId, addressId, items } = req.body;
|
const { userId, addressId, items } = req.body;
|
||||||
|
|
||||||
if (!userId || !addressId || !Array.isArray(items)) {
|
if (!userId || !addressId || !Array.isArray(items)) {
|
||||||
return res.status(400).json({
|
return res
|
||||||
ok: false,
|
.status(400)
|
||||||
error: "Missing or invalid parameters",
|
.json({ ok: false, error: "Missing or invalid parameters" });
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrice(db, itemId) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
db.get(
|
|
||||||
"SELECT price FROM shop_items WHERE name = ?",
|
|
||||||
[itemId],
|
|
||||||
(err, row) => {
|
|
||||||
if (err) return reject({ status: 400, message: err.message });
|
|
||||||
if (!row) return reject({ status: 404, message: "Item not found" });
|
|
||||||
|
|
||||||
resolve(row.price);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBalance(db, username) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
db.get(
|
|
||||||
"SELECT balance FROM users WHERE username = ?",
|
|
||||||
[username],
|
|
||||||
(err, row) => {
|
|
||||||
if (err) return reject({ status: 400, message: err.message });
|
|
||||||
if (!row) return reject({ status: 404, message: "User not found" });
|
|
||||||
|
|
||||||
resolve(row.balance);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const conn = await pool.getConnection();
|
||||||
try {
|
try {
|
||||||
var shopList = {};
|
await conn.beginTransaction();
|
||||||
var total = 0;
|
|
||||||
|
let total = 0;
|
||||||
|
const shopList = {};
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (!item.id || !item.quantity) {
|
if (!item.id || !item.quantity)
|
||||||
return res.status(400).json({
|
return res
|
||||||
ok: false,
|
.status(400)
|
||||||
error: "Invalid item format",
|
.json({ ok: false, error: "Invalid item format" });
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`http://localhost:3100/api/item/${item.id}`,
|
`http://localhost:3100/api/item/${item.id}`,
|
||||||
);
|
);
|
||||||
const stockCount = response.data.item.count;
|
const stockCount = response.data.item.count;
|
||||||
|
if (stockCount < item.quantity)
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ ok: false, error: `Not enough stock for item ${item.id}` });
|
||||||
|
|
||||||
|
const [priceRow] = await conn.query(
|
||||||
|
"SELECT price FROM shop_items WHERE name = ?",
|
||||||
|
[item.id],
|
||||||
|
);
|
||||||
|
if (!priceRow)
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json({ ok: false, error: `Item not found ${item.id}` });
|
||||||
|
const price = priceRow.price;
|
||||||
|
|
||||||
|
const [userRow] = await conn.query(
|
||||||
|
"SELECT balance FROM users WHERE username = ?",
|
||||||
|
[userId],
|
||||||
|
);
|
||||||
|
if (!userRow)
|
||||||
|
return res.status(404).json({ ok: false, error: "User not found" });
|
||||||
|
if (userRow.balance < price * item.quantity)
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({
|
||||||
|
ok: false,
|
||||||
|
error: `Insufficient balance for item ${item.id}`,
|
||||||
|
});
|
||||||
|
|
||||||
if (stockCount < item.quantity) {
|
|
||||||
return res.status(400).json({
|
|
||||||
ok: false,
|
|
||||||
error: `Not enough stock for item ${item.id}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const price = await getPrice(db, item.id);
|
|
||||||
const balance = await getBalance(db, userId);
|
|
||||||
if (balance < price * item.quantity) {
|
|
||||||
return res.status(400).json({
|
|
||||||
ok: false,
|
|
||||||
error: `Insufficient balance for item ${item.id}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const shopId = response.data.shopId;
|
const shopId = response.data.shopId;
|
||||||
if (!shopList[shopId]) {
|
if (!shopList[shopId]) shopList[shopId] = [];
|
||||||
shopList[shopId] = [];
|
shopList[shopId].push({ id: item.id, count: item.quantity });
|
||||||
}
|
|
||||||
shopList[shopId].push({
|
|
||||||
id: item.id,
|
|
||||||
count: item.quantity,
|
|
||||||
});
|
|
||||||
total += price * item.quantity;
|
total += price * item.quantity;
|
||||||
}
|
}
|
||||||
console.log("Shoplist");
|
|
||||||
console.log(shopList);
|
// Send via WebSocket
|
||||||
Object.keys(shopList).forEach((shop) => {
|
Object.keys(shopList).forEach((shop) => {
|
||||||
console.log("Shop");
|
|
||||||
console.log(shop);
|
|
||||||
const request = {
|
const request = {
|
||||||
type: "request",
|
type: "request",
|
||||||
address: addressId,
|
address: addressId,
|
||||||
@ -391,101 +402,78 @@ module.exports = function (app, wss, db) {
|
|||||||
items: shopList[shop],
|
items: shopList[shop],
|
||||||
};
|
};
|
||||||
wss.clients.forEach((client) => {
|
wss.clients.forEach((client) => {
|
||||||
if (client.readyState === 1) {
|
if (client.readyState === 1) client.send(JSON.stringify(request));
|
||||||
client.send(JSON.stringify(request));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// remove total from db
|
|
||||||
db.run(
|
// Deduct total from user
|
||||||
`UPDATE users SET balance = balance - ? WHERE username = ?`,
|
await conn.query(
|
||||||
|
"UPDATE users SET balance = balance - ? WHERE username = ?",
|
||||||
[total, userId],
|
[total, userId],
|
||||||
(err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res.status(500).json({
|
|
||||||
ok: false,
|
|
||||||
error: "Internal server error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
// add total to 'ZareMate'
|
// Add total to 'ZareMate'
|
||||||
db.run(
|
await conn.query(
|
||||||
`UPDATE users SET balance = balance + ? WHERE username = 'ZareMate'`,
|
"UPDATE users SET balance = balance + ? WHERE username = 'ZareMate'",
|
||||||
[total],
|
[total],
|
||||||
(err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res.status(500).json({
|
|
||||||
ok: false,
|
|
||||||
error: "Internal server error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
res.status(200).json({
|
|
||||||
ok: true,
|
await conn.commit();
|
||||||
message: `Successfully purchased items for user ${userId} at ${total}`,
|
res
|
||||||
});
|
.status(200)
|
||||||
|
.json({
|
||||||
|
ok: true,
|
||||||
|
message: `Successfully purchased items for user ${userId} at ${total}`,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
await conn.rollback();
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.status(500).json({
|
res.status(500).json({ ok: false, error: "Internal server error" });
|
||||||
ok: false,
|
} finally {
|
||||||
error: "Internal server error",
|
conn.release();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
app.post("/api/users/transfer", (req, res) => {
|
|
||||||
|
app.post("/api/users/transfer", async (req, res) => {
|
||||||
const { from, to, amount } = req.body;
|
const { from, to, amount } = req.body;
|
||||||
if (!from || !to || !amount) {
|
if (!from || !to || !amount || amount <= 0)
|
||||||
return res.status(400).json({
|
return res.status(400).json({ ok: false, error: "Invalid input" });
|
||||||
ok: false,
|
|
||||||
error: "Missing required fields",
|
const conn = await pool.getConnection();
|
||||||
});
|
try {
|
||||||
|
await conn.beginTransaction();
|
||||||
|
const [fromRow] = await conn.query(
|
||||||
|
"SELECT balance FROM users WHERE username = ?",
|
||||||
|
[from],
|
||||||
|
);
|
||||||
|
if (!fromRow)
|
||||||
|
return res.status(404).json({ ok: false, error: "Sender not found" });
|
||||||
|
if (fromRow.balance < amount)
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ ok: false, error: "Insufficient balance" });
|
||||||
|
|
||||||
|
await conn.query(
|
||||||
|
"UPDATE users SET balance = balance - ? WHERE username = ?",
|
||||||
|
[amount, from],
|
||||||
|
);
|
||||||
|
await conn.query(
|
||||||
|
"UPDATE users SET balance = balance + ? WHERE username = ?",
|
||||||
|
[amount, to],
|
||||||
|
);
|
||||||
|
|
||||||
|
await conn.commit();
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
ok: true,
|
||||||
|
message: `Successfully transferred ${amount} from ${from} to ${to}`,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
await conn.rollback();
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ ok: false, error: "Internal server error" });
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
}
|
}
|
||||||
if (amount <= 0) {
|
|
||||||
return res.status(400).json({
|
|
||||||
ok: false,
|
|
||||||
error: "Invalid amount",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const balance = getBalance(from);
|
|
||||||
if (amount > balance) {
|
|
||||||
return res.status(400).json({
|
|
||||||
ok: false,
|
|
||||||
error: "Insufficient balance",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
db.run(
|
|
||||||
`UPDATE users SET balance = balance - ? WHERE username = ?`,
|
|
||||||
[amount, from],
|
|
||||||
(err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res.status(500).json({
|
|
||||||
ok: false,
|
|
||||||
error: "Internal server error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
db.run(
|
|
||||||
`UPDATE users SET balance = balance + ? WHERE username = ?`,
|
|
||||||
[amount, to],
|
|
||||||
(err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res.status(500).json({
|
|
||||||
ok: false,
|
|
||||||
error: "Internal server error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
res.status(200).json({
|
|
||||||
ok: true,
|
|
||||||
message: `Successfully transferred ${amount} from ${from} to ${to}`,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user