implement basics
This commit is contained in:
parent
7360cbc029
commit
9d1c43e593
|
@ -0,0 +1,170 @@
|
|||
import express, { Request, Response } from "express";
|
||||
import { Database } from "sqlite3";
|
||||
import bcrypt from "bcrypt";
|
||||
import session from "express-session";
|
||||
|
||||
const db = new Database("db.sqlite");
|
||||
|
||||
declare module "express-session" {
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
interface SessionData {
|
||||
user?: number;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await dbRun("CREATE TABLE IF NOT EXISTS version ( version INT );");
|
||||
const dbVersion: { version: number } | undefined
|
||||
= await dbGet("SELECT version FROM version;");
|
||||
|
||||
if (dbVersion && dbVersion.version === 1) {
|
||||
console.log("skip db setup")
|
||||
return;
|
||||
}
|
||||
|
||||
await dbRun(`CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
pass TEXT NOT NULL
|
||||
)`);
|
||||
await dbRun("CREATE UNIQUE INDEX IF NOT EXISTS idx_users_name ON users (name);");
|
||||
|
||||
await dbRun(`CREATE TABLE IF NOT EXISTS highScores (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user INTEGER,
|
||||
score BIGINT NOT NULL,
|
||||
FOREIGN KEY (user) REFERENCES users(id)
|
||||
)`);
|
||||
await dbRun("INSERT INTO version VALUES (1)");
|
||||
})();
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use(session({
|
||||
secret: "your mom",
|
||||
resave: true,
|
||||
}));
|
||||
|
||||
app.get("/", (_req: Request, res: Response) => {
|
||||
res.send("Hello World!");
|
||||
});
|
||||
|
||||
app.get("/leaderboard", async (req: Request, res: Response) => {
|
||||
const { limit = 100, offset = 0 } = req.query;
|
||||
|
||||
const leaders = await dbAll(`
|
||||
select h.score, u.name
|
||||
from highScores h
|
||||
join main.users u on u.id = h.user
|
||||
order by score desc
|
||||
limit ? offset ?`, limit, offset);
|
||||
|
||||
res.json(leaders);
|
||||
});
|
||||
app.put("/leaderboard", async (req: Request, res: Response) => {
|
||||
const { score } = req.body;
|
||||
|
||||
if (!req.session.user) {
|
||||
res.status(403).json({ success: false, message: "unauthorised" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!score) {
|
||||
res.status(400).json({ success: false, message: "provide score" });
|
||||
return;
|
||||
}
|
||||
|
||||
await dbRun("insert into highScores (user, score) VALUES (?, ?)", req.session.user, score);
|
||||
|
||||
res.status(200).json({ success: true });
|
||||
});
|
||||
|
||||
app.post("/login", async (req: Request, res: Response) => {
|
||||
await sleep(Math.random() * 50 + 50);
|
||||
|
||||
const { username, password } = req.body;
|
||||
|
||||
const user: {
|
||||
name: string,
|
||||
pass: string,
|
||||
id: number,
|
||||
} = await dbGet("SELECT * FROM users WHERE name = ?", username);
|
||||
|
||||
if (!user) {
|
||||
res.status(403).json({ success: false, message: "invalid credentials" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await bcrypt.compare(password, user.pass)) {
|
||||
res.status(403).json({ success: false, message: "invalid credentials" });
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise(res => req.session.regenerate(res));
|
||||
|
||||
req.session.user = user.id;
|
||||
|
||||
res.status(200).json({ success: true });
|
||||
});
|
||||
|
||||
app.post("/register", async (req: Request, res: Response) => {
|
||||
await sleep(Math.random() * 50 + 50);
|
||||
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username || !password) {
|
||||
res.status(400).json({ success: false, message: "invalid credentials" });
|
||||
return;
|
||||
}
|
||||
|
||||
const pass = await bcrypt.hash(password, 12);
|
||||
|
||||
const success = await dbRun("INSERT INTO users (name, pass) VALUES (?, ?)", [ username, pass ])
|
||||
.then(() => true, () => false);
|
||||
|
||||
if (!success) {
|
||||
res.status(400).json({ success: false, message: "invalid credentials" });
|
||||
} else {
|
||||
res.status(200).json({ success: true });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/logout", async (req: Request, res: Response) => {
|
||||
await new Promise(res => req.session.destroy(res));
|
||||
res.send(200).json({ success: true });
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log("Server running on port 3000");
|
||||
});
|
||||
|
||||
function dbGet<T>(query: string, ...args: any[]): Promise<T> {
|
||||
return new Promise((res, rej) => {
|
||||
db.get(query, ...args, (err: unknown, result: T) => {
|
||||
if (err) rej(err);
|
||||
else res(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dbAll<T>(query: string, ...args: any[]): Promise<T[]> {
|
||||
return new Promise((res, rej) => {
|
||||
db.all(query, ...args, (err: unknown, result: T[]) => {
|
||||
if (err) rej(err);
|
||||
else res(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dbRun(query: string, ...args: any[]): Promise<void> {
|
||||
return new Promise((res, rej) => {
|
||||
db.run(query, ...args, (err: unknown) => {
|
||||
if (err) rej(err);
|
||||
else res();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
19
package.json
19
package.json
|
@ -1,4 +1,21 @@
|
|||
{
|
||||
"name": "backend",
|
||||
"packageManager": "yarn@4.5.1"
|
||||
"packageManager": "yarn@4.5.1",
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5",
|
||||
"@types/express": "^4",
|
||||
"@types/express-session": "^1",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ts-node-dev ./index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"express": "^4.21.1",
|
||||
"express-session": "^1.18.1",
|
||||
"sqlite3": "^5.1.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue