636 lines
21 KiB
JavaScript
636 lines
21 KiB
JavaScript
const express = require('express');
|
|
const sqlite3 = require('sqlite3').verbose();
|
|
const bcrypt = require('bcrypt');
|
|
const cors = require('cors');
|
|
const bodyParser = require('body-parser');
|
|
const crypto = require('crypto');
|
|
const multer = require('multer'); // Add this to your dependencies
|
|
const storage = multer.memoryStorage();
|
|
const upload = multer({
|
|
storage: storage,
|
|
limits: {
|
|
fileSize: 50 * 1024 * 1024 // 50MB limit
|
|
}
|
|
});
|
|
|
|
const app = express();
|
|
const port = 5000;
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(bodyParser.json({limit: '50mb'})); // Increase JSON payload limit
|
|
app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); // Increase URL-encoded payload limit
|
|
|
|
// SQLite database setup
|
|
const db = new sqlite3.Database('./database.db', (err) => {
|
|
if (err) {
|
|
console.error(err.message);
|
|
} else {
|
|
console.log('Connected to SQLite database.');
|
|
}
|
|
});
|
|
|
|
// Create users table if it doesn't exist
|
|
db.run(`CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE,
|
|
email TEXT UNIQUE,
|
|
password TEXT
|
|
)`);
|
|
|
|
// Create games table if it doesn't exist
|
|
db.run(`CREATE TABLE IF NOT EXISTS games (
|
|
game_id TEXT PRIMARY KEY,
|
|
name TEXT,
|
|
description TEXT,
|
|
game_master_id INTEGER,
|
|
participants TEXT
|
|
)`);
|
|
|
|
|
|
//User Part
|
|
|
|
// Registration route
|
|
app.post('/register', async (req, res) => {
|
|
const { username, email, password } = req.body;
|
|
console.log("Trying to Register: ", username);
|
|
try {
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
const stmt = db.prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
|
|
stmt.run([username, email, hashedPassword], function (err) {
|
|
if (err) {
|
|
return res.status(400).json({ error: 'User already exists or invalid data.' });
|
|
}
|
|
res.status(201).json({ message: 'User registered successfully!', userId: this.lastID });
|
|
});
|
|
stmt.finalize();
|
|
} catch (error) {
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// Login route
|
|
app.post('/login', (req, res) => {
|
|
const { username, password } = req.body;
|
|
db.get('SELECT * FROM users WHERE username = ?', [username], async (err, row) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (!row || !(await bcrypt.compare(password, row.password))) {
|
|
return res.status(400).json({ error: 'Invalid username or password.' });
|
|
}
|
|
res.json({ success: true, message: 'Login successful!', userId: row.id });
|
|
});
|
|
});
|
|
|
|
|
|
// Game Part
|
|
// Fetch games for a specific user
|
|
app.get('/games/:userId', (req, res) => {
|
|
const userId = req.params.userId;
|
|
db.all(
|
|
`SELECT * FROM games WHERE game_master_id = ? OR participants LIKE ?`,
|
|
[userId, `%${userId}%`],
|
|
(err, rows) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
res.json(rows);
|
|
}
|
|
);
|
|
});
|
|
|
|
|
|
// Fetch game details including game master
|
|
app.get('/games/:gameId/master', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
db.get('SELECT * FROM games WHERE game_id = ?', [gameId], (err, row) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (!row) {
|
|
return res.status(404).json({ error: 'Game not found' });
|
|
}
|
|
// Convert game_master_id to number for consistent comparison
|
|
res.json(row);
|
|
});
|
|
});
|
|
|
|
// Join Game
|
|
app.post('/joinGame/:gameId', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
const userId = req.body.userId;
|
|
|
|
db.get('SELECT * FROM games WHERE game_id = ?', [gameId], (err, game) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (!game) {
|
|
return res.status(404).json({ error: 'Game not found' });
|
|
}
|
|
|
|
const participants = JSON.parse(game.participants);
|
|
const gameMasterId = game.game_master_id;
|
|
|
|
// Check if the user is already a participant or the game master
|
|
if (gameMasterId === userId || participants.includes(userId)) {
|
|
return res.status(400).json({ error: 'User is already a participant or the game master.' });
|
|
}
|
|
|
|
participants.push(userId);
|
|
|
|
const stmt = db.prepare('UPDATE games SET participants = ? WHERE game_id = ?');
|
|
stmt.run([JSON.stringify(participants), gameId], function (err) {
|
|
if (err) {
|
|
return res.status(400).json({ error: 'Failed to join game.' });
|
|
}
|
|
res.status(201).json({ message: 'User joined game successfully!', gameId: gameId });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
});
|
|
|
|
|
|
// Create a new game
|
|
app.post('/games', (req, res) => {
|
|
const { name, description, gameMasterId, participants } = req.body;
|
|
const stmt = db.prepare('INSERT INTO games (name, description, game_master_id, participants) VALUES (?, ?, ?, ?)');
|
|
stmt.run([name, description, gameMasterId, JSON.stringify(participants)], function (err) {
|
|
if (err) {
|
|
return res.status(400).json({ error: 'Failed to create game.' });
|
|
}
|
|
res.status(201).json({ message: 'Game created successfully!', gameId: this.lastID });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
app.post('/createGame', (req, res) => {
|
|
const { name, description, gameMasterId, participants } = req.body;
|
|
const gameId = crypto.randomBytes(4).toString('hex');
|
|
|
|
// Convert gameMasterId to integer
|
|
const parsedGameMasterId = parseInt(gameMasterId);
|
|
|
|
const stmt = db.prepare('INSERT INTO games (game_id, name, description, game_master_id, participants) VALUES (?, ?, ?, ?, ?)');
|
|
stmt.run([gameId, name, description, parsedGameMasterId, JSON.stringify(participants)], function (err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(400).json({ error: 'Failed to create game.' });
|
|
}
|
|
res.status(201).json({ message: 'Game created successfully!', gameId: gameId });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Master Part
|
|
// Fetch game Participant Characters
|
|
app.get('/games/:gameId/playerchars', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
db.all('SELECT * FROM PlayerCharacter WHERE GameID = ?', [gameId], (err, rows) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
// Process each row and convert BLOB to base64 if image exists
|
|
const processedRows = rows.map(row => {
|
|
if (row.Img) {
|
|
row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`;
|
|
}
|
|
return row;
|
|
});
|
|
res.json(processedRows);
|
|
});
|
|
});
|
|
|
|
// Fetch game NPCs
|
|
app.get('/games/:gameId/npcs', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
db.all('SELECT * FROM NPC WHERE GameID = ?', [gameId], (err, rows) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
// Process each row and convert BLOB to base64 if image exists
|
|
const processedRows = rows.map(row => {
|
|
if (row.Img) {
|
|
row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`;
|
|
}
|
|
return row;
|
|
});
|
|
res.json(processedRows);
|
|
});
|
|
});
|
|
|
|
// Fetch game Items
|
|
app.get('/games/:gameId/items', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
db.all(`
|
|
SELECT i.*,
|
|
COALESCE(pc.CharName, n.Name) as OwnerName
|
|
FROM Item i
|
|
LEFT JOIN PlayerCharacter pc ON i.OwnerID = pc.CharID
|
|
LEFT JOIN NPC n ON i.OwnerID = n.NPCID
|
|
WHERE i.GameID = ?`,
|
|
[gameId],
|
|
(err, rows) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
// Convert image data to base64 and store as Img property
|
|
const processedRows = rows.map(row => {
|
|
if (row.img) { // Database column is lowercase
|
|
row.Img = `data:image/jpeg;base64,${row.img.toString('base64')}`; // Store as uppercase Img
|
|
}
|
|
return row;
|
|
});
|
|
res.json(processedRows);
|
|
}
|
|
);
|
|
});
|
|
|
|
|
|
// Player Part
|
|
|
|
// Create Player Character
|
|
app.post('/games/character/create', upload.single('image'), (req, res) => {
|
|
const {
|
|
charName, race, sex, age, job, description,
|
|
maxHealth, maxMana, strength, dexterity, agility, endurance,
|
|
gameId, playerId
|
|
} = req.body;
|
|
|
|
const imageBuffer = req.file ? req.file.buffer : null;
|
|
|
|
const stmt = db.prepare(`
|
|
INSERT INTO PlayerCharacter (
|
|
GameID, PlayerID, CharName, Race, Sex, Age, Job,
|
|
description, maxHealth, currentHealth, maxMana, currentMana,
|
|
Strength, Dexterity, Agility, Endurance, Level, Gold, Img
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?)
|
|
`);
|
|
|
|
stmt.run([
|
|
gameId, playerId, charName, race, sex, age, job,
|
|
description, maxHealth, maxHealth, maxMana, maxMana,
|
|
strength, dexterity, agility, endurance, imageBuffer
|
|
], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
res.status(201).json({
|
|
message: 'Character created successfully!',
|
|
characterId: this.lastID
|
|
});
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Fetch all Player Characters of Player
|
|
app.get('/games/:userId/characters', (req, res) => {
|
|
const userId = req.params.userId;
|
|
db.all('SELECT * FROM PlayerCharacter WHERE PlayerId = ?', [userId], (err, rows) => {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
|
|
// Process each row and convert BLOB to base64 if image exists
|
|
const processedRows = rows.map(row => {
|
|
if (row.Img) {
|
|
row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`;
|
|
}
|
|
return row;
|
|
});
|
|
|
|
res.json(processedRows);
|
|
});
|
|
});
|
|
|
|
// Fetch Player Character
|
|
app.get('/games/:gameId/:userId/character', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
const userId = req.params.userId;
|
|
console.log(`Fetching character for gameId: ${gameId}, userId: ${userId}`); // Debug output
|
|
db.get('SELECT * FROM PlayerCharacter WHERE GameID = ? AND PlayerID = ?', [gameId, userId], (err, row) => {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (!row) {
|
|
return res.status(404).json({ error: 'No character found' });
|
|
}
|
|
|
|
// Convert BLOB to base64 string if image exists
|
|
if (row.Img) {
|
|
row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`;
|
|
}
|
|
|
|
res.json(row);
|
|
});
|
|
});
|
|
|
|
// Update character image
|
|
app.put('/games/:gameId/:userId/character/image', upload.single('image'), (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
const userId = req.params.userId;
|
|
const imageBuffer = req.file.buffer;
|
|
|
|
const stmt = db.prepare('UPDATE PlayerCharacter SET Img = ? WHERE GameID = ? AND PlayerID = ?');
|
|
stmt.run([imageBuffer, gameId, userId], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
res.json({ message: 'Image updated successfully!' });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Update Player Character Description
|
|
app.put('/games/:gameId/:userId/character', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
const userId = req.params.userId;
|
|
const { description } = req.body;
|
|
|
|
const stmt = db.prepare('UPDATE PlayerCharacter SET description = ? WHERE GameID = ? AND PlayerID = ?');
|
|
stmt.run([description, gameId, userId], function (err) {
|
|
if (err) {
|
|
console.error('Database error:', err); // Debug output
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
res.json({ message: 'Description updated successfully!' });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Update Player Character
|
|
app.put('/games/character/:charId', async (req, res) => {
|
|
const charId = req.params.charId;
|
|
const {
|
|
charName, race, sex, age, job, description,
|
|
maxHealth, currentHealth, maxMana, currentMana,
|
|
strength, dexterity, agility, endurance,
|
|
level, gold
|
|
} = req.body;
|
|
|
|
const stmt = db.prepare(`
|
|
UPDATE PlayerCharacter
|
|
SET CharName = ?, Race = ?, Sex = ?, Age = ?, Job = ?,
|
|
Description = ?, MaxHealth = ?, CurrentHealth = ?,
|
|
MaxMana = ?, CurrentMana = ?, Strength = ?,
|
|
Dexterity = ?, Agility = ?, Endurance = ?,
|
|
Level = ?, Gold = ?
|
|
WHERE CharID = ?
|
|
`);
|
|
|
|
stmt.run([
|
|
charName, race, sex, age, job, description,
|
|
maxHealth, currentHealth, maxMana, currentMana,
|
|
strength, dexterity, agility, endurance,
|
|
level, gold, charId
|
|
], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (this.changes === 0) {
|
|
return res.status(404).json({ error: 'Character not found' });
|
|
}
|
|
res.json({ message: 'Character updated successfully!' });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Fetch Player Items
|
|
app.get('/games/:gameId/:charId/items', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
const charId = req.params.charId;
|
|
console.log(`Fetching Items for GameID: ${gameId}, CharID: ${charId}`);
|
|
db.all('SELECT * FROM Item WHERE GameID = ? AND OwnerID = ?', [gameId, charId], (err, rows) => {
|
|
if (err) {
|
|
console.error('Database error:', err); // Debug output
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
console.log(rows); // Debug output
|
|
res.json(rows);
|
|
});
|
|
});
|
|
|
|
// Item Part
|
|
// Create Item
|
|
app.post('/games/item/create', upload.single('image'), (req, res) => {
|
|
const {
|
|
ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability,
|
|
GoldValue, Abilities, GameID, AP
|
|
} = req.body;
|
|
|
|
const imageBuffer = req.file ? req.file.buffer : null;
|
|
|
|
const stmt = db.prepare(`
|
|
INSERT INTO Item (
|
|
ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability,
|
|
GoldValue, Abilities, GameID, OwnerID, img, AP
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?)
|
|
`);
|
|
|
|
stmt.run([
|
|
ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability,
|
|
GoldValue, Abilities, GameID, imageBuffer, AP
|
|
], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
res.status(201).json({
|
|
message: 'Item created successfully!',
|
|
itemId: this.lastID
|
|
});
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Set Item Owner
|
|
app.post('/games/:charId/:itemId/owner', (req, res) => {
|
|
const gameId = req.params.gameId;
|
|
const itemId = req.params.itemId;
|
|
const ownerId = req.body.ownerId;
|
|
db.run('UPDATE Item SET OwnerID = ? WHERE ItemID = ?', [ownerId, gameId, itemId], function (err) {
|
|
if (err) {
|
|
return res.status(400).json({ error: 'Failed to update item owner.' });
|
|
}
|
|
res.json({ message: 'Item owner updated successfully!' });
|
|
});
|
|
})
|
|
|
|
// Update Item details including owner
|
|
app.put('/games/item/:itemId', (req, res) => {
|
|
const itemId = req.params.itemId;
|
|
const {
|
|
ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability,
|
|
GoldValue, Abilities, OwnerID, AP
|
|
} = req.body;
|
|
|
|
// Validate required fields
|
|
if (!ItemName) {
|
|
return res.status(400).json({ error: 'Item name is required' });
|
|
}
|
|
|
|
const stmt = db.prepare(`
|
|
UPDATE Item
|
|
SET ItemName = ?, Type = ?, Art = ?, Rarity = ?,
|
|
MaxDurability = ?, CurrentDurability = ?, GoldValue = ?,
|
|
Abilities = ?, OwnerID = ?, AP = ?
|
|
WHERE ItemID = ?
|
|
`);
|
|
|
|
stmt.run([
|
|
ItemName, Type, Art, Rarity,
|
|
MaxDurability, CurrentDurability, GoldValue,
|
|
Abilities, OwnerID, AP, itemId
|
|
], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (this.changes === 0) {
|
|
return res.status(404).json({ error: 'Item not found' });
|
|
}
|
|
res.json({ message: 'Item updated successfully!' });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
//NPC part
|
|
|
|
// Create NPC
|
|
app.post('/games/npc/create', upload.single('image'), (req, res) => {
|
|
const {
|
|
Name, Race, Sex, Age, Job, Description,
|
|
MaxHealth, MaxMana, Strength, Dexterity,
|
|
Agility, Endurance, Allied, Level, GameID
|
|
} = req.body;
|
|
|
|
const imageBuffer = req.file ? req.file.buffer : null;
|
|
|
|
const stmt = db.prepare(`
|
|
INSERT INTO NPC (
|
|
GameID, Name, Race, Sex, Age, Job, Description,
|
|
MaxHealth, CurrentHealth, MaxMana, CurrentMana,
|
|
Strength, Dexterity, Agility, Endurance,
|
|
Level, Allied, Img
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`);
|
|
|
|
stmt.run([
|
|
GameID, Name, Race, Sex, Age, Job, Description,
|
|
MaxHealth, MaxHealth, MaxMana, MaxMana,
|
|
Strength, Dexterity, Agility, Endurance,
|
|
Level, Allied, imageBuffer
|
|
], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
res.status(201).json({
|
|
message: 'NPC created successfully!',
|
|
npcId: this.lastID
|
|
});
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Update NPC details
|
|
app.put('/games/npc/:npcId', upload.single('image'), (req, res) => {
|
|
const npcId = req.params.npcId;
|
|
const {
|
|
Name, Race, Sex, Age, Job, Description,
|
|
MaxHealth, CurrentHealth, MaxMana, CurrentMana,
|
|
Strength, Dexterity, Agility, Endurance,
|
|
Level, Allied
|
|
} = req.body;
|
|
|
|
let updateQuery = `
|
|
UPDATE NPC SET
|
|
Name = ?, Race = ?, Sex = ?, Age = ?, Job = ?,
|
|
Description = ?, MaxHealth = ?, CurrentHealth = ?,
|
|
MaxMana = ?, CurrentMana = ?, Strength = ?,
|
|
Dexterity = ?, Agility = ?, Endurance = ?,
|
|
Level = ?, Allied = ?
|
|
`;
|
|
|
|
let params = [
|
|
Name, Race, Sex, Age, Job, Description,
|
|
MaxHealth, CurrentHealth, MaxMana, CurrentMana,
|
|
Strength, Dexterity, Agility, Endurance,
|
|
Level, Allied
|
|
];
|
|
|
|
// If new image is uploaded, add it to the update
|
|
if (req.file) {
|
|
updateQuery += `, Img = ?`;
|
|
params.push(req.file.buffer);
|
|
}
|
|
|
|
updateQuery += ` WHERE NPCID = ?`;
|
|
params.push(npcId);
|
|
|
|
const stmt = db.prepare(updateQuery);
|
|
stmt.run(params, function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (this.changes === 0) {
|
|
return res.status(404).json({ error: 'NPC not found' });
|
|
}
|
|
res.json({ message: 'NPC updated successfully!' });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
// Get NPC details
|
|
app.get('/games/npc/:npcId', (req, res) => {
|
|
const npcId = req.params.npcId;
|
|
db.get('SELECT * FROM NPC WHERE NPCID = ?', [npcId], (err, row) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (!row) {
|
|
return res.status(404).json({ error: 'NPC not found' });
|
|
}
|
|
|
|
// Convert BLOB to base64 string if image exists
|
|
if (row.Img) {
|
|
row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`;
|
|
}
|
|
|
|
res.json(row);
|
|
});
|
|
});
|
|
|
|
// Update NPC image
|
|
app.put('/games/npc/:npcId/image', upload.single('image'), (req, res) => {
|
|
const npcId = req.params.npcId;
|
|
const imageBuffer = req.file.buffer;
|
|
|
|
const stmt = db.prepare('UPDATE NPC SET Img = ? WHERE NPCID = ?');
|
|
stmt.run([imageBuffer, npcId], function(err) {
|
|
if (err) {
|
|
console.error('Database error:', err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
if (this.changes === 0) {
|
|
return res.status(404).json({ error: 'NPC not found' });
|
|
}
|
|
res.json({ message: 'NPC image updated successfully!' });
|
|
});
|
|
stmt.finalize();
|
|
});
|
|
|
|
|
|
|
|
|
|
// Start the server
|
|
app.listen(port, () => {
|
|
console.log(`Server running on http://localhost:${port}`);
|
|
});
|