implement basics

This commit is contained in:
CodedSakura 2024-11-15 23:18:39 +02:00
parent 7360cbc029
commit 9d1c43e593
4 changed files with 3225 additions and 1 deletions

170
index.ts Normal file
View File

@ -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));
}

View File

@ -1,4 +1,21 @@
{ {
"name": "backend", "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"
}
} }

10
tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

3027
yarn.lock

File diff suppressed because it is too large Load Diff