first commit

This commit is contained in:
2026-01-02 15:49:01 +03:00
commit 4348f76a7c
80 changed files with 10133 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
import { Request, Response, NextFunction } from "express";
import { verifyToken } from "./auth.service"
export const requireAuth = (req: Request, res: Response, next: NextFunction) => {
const token = req.cookies?.["qbuffer_token"];
if (!token) {
return res.status(401).json({ error: "Unauthorized" });
}
try {
const payload = verifyToken(token);
req.user = payload;
return next();
} catch (error) {
return res.status(401).json({ error: "Unauthorized" });
}
};
declare module "express-serve-static-core" {
interface Request {
user?: { username: string };
}
}

View File

@@ -0,0 +1,64 @@
import { Router } from "express";
import rateLimit from "express-rate-limit";
import { signToken, verifyCredentials, verifyToken } from "./auth.service"
import { isDev } from "../config"
const router = Router();
const loginLimiter = rateLimit({
windowMs: 60_000,
max: 5,
standardHeaders: true,
legacyHeaders: false,
});
router.post("/login", loginLimiter, async (req, res) => {
const { username, password } = req.body ?? {};
if (!username || !password) {
return res.status(400).json({ error: "Missing credentials" });
}
const user = await verifyCredentials(username, password);
if (!user) {
return res.status(401).json({ error: "Invalid credentials" });
}
const token = signToken({ username: user.username });
res.cookie("qbuffer_token", token, {
httpOnly: true,
sameSite: "lax",
secure: !isDev,
});
return res.json({ username: user.username });
});
router.post("/logout", (_req, res) => {
res.clearCookie("qbuffer_token");
return res.json({ ok: true });
});
router.get("/me", (req, res) => {
const token = req.cookies?.["qbuffer_token"];
if (!token) {
return res.status(401).json({ error: "Unauthorized" });
}
try {
const payload = verifyToken(token);
return res.json({ ok: true, username: payload.username });
} catch (error) {
return res.status(401).json({ error: "Unauthorized" });
}
});
router.get("/socket-token", (req, res) => {
const token = req.cookies?.["qbuffer_token"];
if (!token) {
return res.status(401).json({ error: "Unauthorized" });
}
try {
verifyToken(token);
return res.json({ token });
} catch (error) {
return res.status(401).json({ error: "Unauthorized" });
}
});
export default router;

View File

@@ -0,0 +1,48 @@
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import { config } from "../config"
import { readDb, writeDb } from "../storage/jsondb"
import { nowIso } from "../utils/time"
import { User } from "../types"
const ensureSeedUser = async (): Promise<User | null> => {
if (!config.appUsername || !config.appPassword) {
return null;
}
const db = await readDb();
const existing = db.users.find((user) => user.username === config.appUsername);
if (existing) {
return existing;
}
const passwordHash = await bcrypt.hash(config.appPassword, 10);
const newUser: User = {
username: config.appUsername,
passwordHash,
createdAt: nowIso(),
};
db.users.push(newUser);
await writeDb(db);
return newUser;
};
export const initAuth = async () => {
await ensureSeedUser();
};
export const verifyCredentials = async (username: string, password: string) => {
const db = await readDb();
const user = db.users.find((u) => u.username === username);
if (!user) {
return null;
}
const match = await bcrypt.compare(password, user.passwordHash);
return match ? user : null;
};
export const signToken = (payload: { username: string }) => {
return jwt.sign(payload, config.jwtSecret, { expiresIn: "7d" });
};
export const verifyToken = (token: string) => {
return jwt.verify(token, config.jwtSecret) as { username: string };
};