feat(sprint1-step2): core economy TS + useEconomy hook (lazy calc) + 13 tests vitest

This commit is contained in:
2026-03-17 06:36:51 +01:00
parent c414cf2d07
commit c69da320cc
13 changed files with 2627 additions and 174 deletions

View File

@@ -0,0 +1,57 @@
const tables = require("../tables");
/**
* Middleware verifyOAuth — Token Introspection via SuperOAuth.
*
* Flow :
* 1. Extraire le token du header x-auth-token
* 2. Appeler SuperOAuth POST /api/v1/auth/token/validate
* 3. Résoudre l'utilisateur local par super_oauth_id
* 4. req.user = localUser.id (integer) — verifySelf inchangé
*/
const verifyOAuth = async (req, res, next) => {
const token = req.header("x-auth-token");
if (!token) {
return res.status(401).json({ message: "Access denied. No token provided." });
}
const superOAuthUrl = process.env.SUPER_OAUTH_URL;
if (!superOAuthUrl) {
console.error("verifyOAuth — SUPER_OAUTH_URL not configured");
return res.status(500).json({ message: "Auth service not configured." });
}
try {
const response = await fetch(`${superOAuthUrl}/api/v1/auth/token/validate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
const data = await response.json();
if (!response.ok || !data.data?.valid || !data.data?.user) {
return res.status(401).json({ message: "Invalid or expired token." });
}
if (!data.data.user.isActive) {
return res.status(401).json({ message: "Account is disabled." });
}
const superOAuthId = data.data.user.id;
const localUser = await tables.users.getBySuperOAuthId(superOAuthId);
if (!localUser) {
return res.status(401).json({ message: "Account not linked. Please log in via SuperOAuth." });
}
req.user = localUser.id;
return next();
} catch (err) {
console.error("verifyOAuth — auth service unreachable", err);
return res.status(500).json({ message: "Authentication service unreachable." });
}
};
module.exports = verifyOAuth;