miauw
This commit is contained in:
@@ -3,115 +3,234 @@ import { pool } from './db.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /healthcheck:
|
||||
* get:
|
||||
* summary: Check if the API is running
|
||||
* responses:
|
||||
* 200:
|
||||
* description: API is healthy
|
||||
*/
|
||||
router.get("/healthcheck", (req, res) => {
|
||||
res.json({ status: "ok" })
|
||||
});
|
||||
|
||||
router.get("/users", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM users`);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
router.get("/users/:id", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM users WHERE id = ?`, [req.params.id]);
|
||||
res.json(rows[0] || null);
|
||||
});
|
||||
|
||||
router.post("/users", async (req, res) => {
|
||||
const { username } = req.body;
|
||||
const [result] = await pool.query(`INSERT INTO users (username) VALUES (?)`, [username]);
|
||||
res.status(201).json({ id: result.insertId, username });
|
||||
});
|
||||
const { username, input_method = 0 } = req.body;
|
||||
|
||||
router.put("/users/:id", async (req, res) => {
|
||||
const { username } = req.body;
|
||||
await pool.query(`UPDATE users SET username = ? WHERE id = ?`, [username, req.params.id]);
|
||||
res.json({ id: req.params.id, username });
|
||||
});
|
||||
const [result] = await pool.query(
|
||||
"INSERT INTO users (username, input_method) VALUES (?, ?)",
|
||||
[username, input_method]
|
||||
);
|
||||
|
||||
router.delete("/users/:id", async (req, res) => {
|
||||
await pool.query(`DELETE FROM users WHERE id = ?`, [req.params.id]);
|
||||
res.status(204).send();
|
||||
});
|
||||
|
||||
router.get("/users/:id/settings", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM user_settings WHERE user = ?`, [req.params.id]);
|
||||
res.json(rows[0] || null);
|
||||
});
|
||||
|
||||
router.put("/users/:id/settings", async (req, res) => {
|
||||
const { input_method } = req.body;
|
||||
await pool.query(`
|
||||
INSERT INTO user_settings (user, input_method)
|
||||
VALUES (?, ?)
|
||||
ON DUPLICATE KEY UPDATE input_method = ?
|
||||
`, [req.params.id, input_method, input_method]);
|
||||
res.json({ user: req.params.id, input_method });
|
||||
const [userRow] = await pool.query("SELECT * FROM users WHERE id = ?", [result.insertId]);
|
||||
res.status(201).json(userRow[0]);
|
||||
});
|
||||
|
||||
router.get("/games", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM games`);
|
||||
const [rows] = await pool.query("SELECT * FROM current_games");
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
router.get("/games/:id", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM games WHERE id = ?`, [req.params.id]);
|
||||
const [rows] = await pool.query("SELECT * FROM current_games WHERE id = ?", [req.params.id]);
|
||||
res.json(rows[0] || null);
|
||||
});
|
||||
|
||||
router.post("/games", async (req, res) => {
|
||||
const [result] = await pool.query(`INSERT INTO games (created_at) VALUES (NOW())`);
|
||||
res.status(201).json({ id: result.insertId, created_at: new Date() });
|
||||
});
|
||||
const { user, is_local = false } = req.body;
|
||||
const conn = await pool.getConnection();
|
||||
|
||||
router.put("/games/:id", async (req, res) => {
|
||||
const { winner } = req.body;
|
||||
await pool.query(`UPDATE games SET winner = ? WHERE id = ?`, [winner, req.params.id]);
|
||||
res.json({ id: req.params.id, winner });
|
||||
});
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
|
||||
router.delete("/games/:id", async (req, res) => {
|
||||
await pool.query(`DELETE FROM games WHERE id = ?`, [req.params.id]);
|
||||
res.status(204).send();
|
||||
});
|
||||
const [gameResult] = await conn.query(
|
||||
"INSERT INTO current_games (is_open, is_local, current_playing_user) VALUES (?, ?, ?)",
|
||||
[true, is_local, user]
|
||||
);
|
||||
|
||||
router.get("/games/:id/players", async (req, res) => {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT u.id, u.username
|
||||
FROM game_players gp
|
||||
const gameId = gameResult.insertId;
|
||||
|
||||
await conn.query("INSERT INTO game_players (game, user, is_creator) VALUES (?, ?)", [gameId, user, true]);
|
||||
|
||||
const [gameRow] = await conn.query("SELECT * FROM current_games WHERE id = ?", [gameId]);
|
||||
const [players] = await conn.query(
|
||||
`SELECT u.id, u.username FROM game_players gp
|
||||
JOIN users u ON gp.user = u.id
|
||||
WHERE gp.game = ?
|
||||
`, [req.params.id]);
|
||||
res.json(rows);
|
||||
WHERE gp.game = ?`,
|
||||
[gameId]
|
||||
);
|
||||
|
||||
await conn.commit();
|
||||
res.status(201).json({ game: gameRow[0], players });
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/games/:id/players", async (req, res) => {
|
||||
const { user } = req.body;
|
||||
await pool.query(`INSERT IGNORE INTO game_players (game, user) VALUES (?, ?)`, [req.params.id, user]);
|
||||
res.status(201).json({ game: req.params.id, user });
|
||||
const gameId = req.params.id;
|
||||
|
||||
const conn = await pool.getConnection();
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
|
||||
const [gameRows] = await conn.query(
|
||||
"SELECT * FROM current_games WHERE id = ? AND is_open = TRUE AND is_local = FALSE",
|
||||
[gameId]
|
||||
);
|
||||
|
||||
if (gameRows.length === 0) {
|
||||
return res.status(400).json({ error: "Game is not open or is local" });
|
||||
}
|
||||
|
||||
await conn.query(
|
||||
"INSERT IGNORE INTO game_players (game, user, is_creator) VALUES (?, ?, ?)",
|
||||
[gameId, user, false]
|
||||
);
|
||||
|
||||
const [players] = await conn.query(
|
||||
`SELECT u.id, u.username FROM game_players gp
|
||||
JOIN users u ON gp.user = u.id
|
||||
WHERE gp.game = ?`,
|
||||
[gameId]
|
||||
);
|
||||
|
||||
await conn.commit();
|
||||
res.status(201).json({ game: gameId, players });
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/games/:id/players/:user", async (req, res) => {
|
||||
await pool.query(`DELETE FROM game_players WHERE game = ? AND user = ?`, [req.params.id, req.params.user]);
|
||||
res.status(204).send();
|
||||
router.patch("/games/:id/lock", async (req, res) => {
|
||||
const { user } = req.body;
|
||||
const gameId = req.params.id;
|
||||
|
||||
const conn = await pool.getConnection();
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
|
||||
const [creatorRows] = await pool.query(
|
||||
"SELECT * FROM game_players WHERE game = ? AND user = ? AND is_creator = TRUE",
|
||||
[gameId, user]
|
||||
);
|
||||
|
||||
if (creatorRows.length === 0) {
|
||||
return res.status(403).json({ error: "Only the creator can lock the game" });
|
||||
}
|
||||
|
||||
const [players] = await conn.query(
|
||||
`SELECT user FROM game_players WHERE game = ?`,
|
||||
[gameId]
|
||||
);
|
||||
|
||||
const turnOrder = players.map(p => p.user);
|
||||
for (let i = turnOrder.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[turnOrder[i], turnOrder[j]] = [turnOrder[j], turnOrder[i]];
|
||||
}
|
||||
|
||||
await conn.query(
|
||||
`UPDATE current_games SET is_open = FALSE, turn_order = ? WHERE id = ?`,
|
||||
[JSON.stringify(turnOrder), gameId]
|
||||
);
|
||||
|
||||
await conn.commit();
|
||||
res.json({ message: "Game locked", turnOrder });
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
|
||||
await pool.query("UPDATE current_games SET is_open = FALSE WHERE id = ?", [gameId]);
|
||||
res.json({ message: "Game locked" });
|
||||
});
|
||||
|
||||
router.get("/games/:id/players", async (req, res) => {
|
||||
const [players] = await pool.query(
|
||||
`SELECT u.id, u.username FROM game_players gp
|
||||
JOIN users u ON gp.user = u.id
|
||||
WHERE gp.game = ?`,
|
||||
[req.params.id]
|
||||
);
|
||||
res.json(players);
|
||||
});
|
||||
|
||||
router.get("/games/:id/turns", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM turns WHERE game = ? ORDER BY round_number`, [req.params.id]);
|
||||
const [rows] = await pool.query(
|
||||
"SELECT * FROM turns WHERE game = ? ORDER BY round_number",
|
||||
[req.params.id]
|
||||
);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
router.post("/games/:id/turns", async (req, res) => {
|
||||
const { user, round_number, start_points, first_throw, second_throw, third_throw, end_points } = req.body;
|
||||
const [result] = await pool.query(`
|
||||
INSERT INTO turns (game, user, round_number, start_points, first_throw, second_throw, third_throw, end_points)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [req.params.id, user, round_number, start_points, first_throw, second_throw, third_throw, end_points]);
|
||||
res.status(201).json({ id: result.insertId });
|
||||
const gameId = req.params.id;
|
||||
const conn = await pool.getConnection();
|
||||
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
|
||||
const [gameRows] = await conn.query(
|
||||
"SELECT current_playing_user FROM current_games WHERE id = ? FOR UPDATE",
|
||||
[gameId]
|
||||
);
|
||||
|
||||
if (gameRows.length === 0) {
|
||||
return res.status(404).json({ error: "Game not found" });
|
||||
}
|
||||
|
||||
const game = gameRows[0];
|
||||
const turnOrder = JSON.parse(game.turn_order || '[]');
|
||||
|
||||
if (game.current_playing_user !== user) {
|
||||
return res.status(403).json({ error: "Not your turn" });
|
||||
}
|
||||
|
||||
const [result] = await conn.query(
|
||||
`INSERT INTO turns
|
||||
(game, user, round_number, start_points, first_throw, second_throw, third_throw, end_points)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[gameId, user, round_number, start_points, first_throw, second_throw, third_throw, end_points]
|
||||
);
|
||||
|
||||
const currentIndex = turnOrder.indexOf(user);
|
||||
const nextIndex = (currentIndex + 1) % turnOrder.length;
|
||||
const nextPlayer = turnOrder[nextIndex];
|
||||
|
||||
await conn.query(
|
||||
"UPDATE current_games SET current_playing_user = ? WHERE id = ?",
|
||||
[nextPlayer, gameId]
|
||||
);
|
||||
|
||||
await conn.commit();
|
||||
|
||||
res.status(201).json({
|
||||
turnId: result.insertId,
|
||||
nextPlayer
|
||||
});
|
||||
} catch (err) {
|
||||
await conn.rollback();
|
||||
throw err;
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/turns/:id", async (req, res) => {
|
||||
const [rows] = await pool.query(`SELECT * FROM turns WHERE id = ?`, [req.params.id]);
|
||||
const [rows] = await pool.query("SELECT * FROM turns WHERE id = ?", [req.params.id]);
|
||||
res.json(rows[0] || null);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,56 +21,60 @@ const migrations = [
|
||||
name: "init",
|
||||
up: async (conn) => {
|
||||
await conn.query(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
CREATE TABLE users (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
username VARCHAR(255) NOT NULL
|
||||
username VARCHAR(32),
|
||||
input_method INT
|
||||
)
|
||||
`);
|
||||
|
||||
await conn.query(`
|
||||
CREATE TABLE IF NOT EXISTS user_settings (
|
||||
user INT NOT NULL,
|
||||
input_method INT,
|
||||
PRIMARY KEY (user),
|
||||
FOREIGN KEY (user) REFERENCES users(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
await conn.query(`
|
||||
CREATE TABLE IF NOT EXISTS games (
|
||||
CREATE TABLE games (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
winner INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (winner) REFERENCES users(id) ON DELETE SET NULL
|
||||
FOREGIN KEY (winner) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
await conn.query(`
|
||||
CREATE TABLE IF NOT EXISTS game_players (
|
||||
CREATE TABLE current_games (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
game INT NOT NULL,
|
||||
user INT NOT NULL,
|
||||
FOREIGN KEY (game) REFERENCES games(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE (game, user)
|
||||
is_open BOOL,
|
||||
is_local BOOL,
|
||||
current_playing_user INT,
|
||||
turn_order JSON DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (current_playing_user) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
await conn.query(`
|
||||
CREATE TABLE IF NOT EXISTS turns (
|
||||
CREATE TABLE game_players (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
game INT NOT NULL,
|
||||
user INT NOT NULL,
|
||||
round_number INT NOT NULL,
|
||||
game INT,
|
||||
user INT,
|
||||
is_creator BOOL,
|
||||
FOREIGN KEY (game) REFERENCES current_games(id),
|
||||
FOREIGN KEY (user) REFERENCES users(id)
|
||||
)
|
||||
`)
|
||||
|
||||
await conn.query(`
|
||||
CREATE TABLE turns (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
game INT,
|
||||
user INT,
|
||||
round_number INT,
|
||||
start_points INT,
|
||||
first_throw INT,
|
||||
second_throw INT,
|
||||
third_throw INT,
|
||||
end_points INT,
|
||||
FOREIGN KEY (game) REFERENCES games(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user) REFERENCES users(id) ON DELETE CASCADE
|
||||
FOREIGN KEY (game) REFERENCES current_games(id),
|
||||
FOREIGN KEY (user) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
`)
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import swaggerJsdoc from "swagger-jsdoc";
|
||||
import swaggerUi from "swagger-ui-express";
|
||||
import https from 'https';
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
@@ -10,6 +12,16 @@ import { fileURLToPath } from 'url';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.join(path.dirname(__filename), ".."); // going back a dir cuz code is in src/
|
||||
const app = express();
|
||||
const specs = swaggerJsdoc({
|
||||
definition: {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Analogue Game Assistent API",
|
||||
version: "1.0.0",
|
||||
},
|
||||
},
|
||||
apis: ["./src/api.js"],
|
||||
});
|
||||
|
||||
initDB();
|
||||
|
||||
@@ -19,13 +31,12 @@ app.use(express.urlencoded({ extended: true }))
|
||||
app.use(express.static(path.join(__dirname, "public"), {
|
||||
dotfiles: "ignore"
|
||||
}));
|
||||
app.use("/api/docs", swaggerUi.serve, swaggerUi.setup(specs));
|
||||
app.use("/api", apiRouter);
|
||||
|
||||
const options = {
|
||||
const server = https.createServer({
|
||||
key: fs.readFileSync('server.key'),
|
||||
cert: fs.readFileSync('server.cert')
|
||||
};
|
||||
|
||||
const server = https.createServer(options, app).listen(5555, () => {
|
||||
}, app).listen(5555, () => {
|
||||
console.log(`Server running on http://localhost:${server.address().port}`);
|
||||
});
|
||||
Reference in New Issue
Block a user